1 /*
2     SPDX-FileCopyrightText: 2020 Jonathan L. Verner <jonathan.verner@matfyz.cz>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "simplecommitform.h"
8 
9 #include <KColorScheme>
10 #include <KLocalizedString>
11 #include <KMessageWidget>
12 
13 #include <QAction>
14 #include <QFontDatabase>
15 #include <QGuiApplication>
16 #include <QHBoxLayout>
17 #include <QLabel>
18 #include <QLineEdit>
19 #include <QPainter>
20 #include <QPushButton>
21 #include <QTextEdit>
22 #include <QToolButton>
23 #include <QVBoxLayout>
24 
textIcon(const QString & str,const QColor & fg=Qt::black,const QColor & bg=Qt::white)25 QPixmap textIcon(const QString& str, const QColor& fg = Qt::black, const QColor& bg = Qt::white)
26 {
27     QPixmap ret(40, 40);
28     QPainter p(&ret);
29     QFont font = p.font();
30     font.setPixelSize(0.625 * 40);
31     p.setFont(font);
32     ret.fill(bg);
33     p.setPen(fg);
34     p.drawText(ret.rect(), Qt::AlignCenter, str);
35     return ret;
36 }
37 
SimpleCommitForm(QWidget * parent)38 SimpleCommitForm::SimpleCommitForm(QWidget* parent)
39     : QWidget(parent)
40     , m_commitBtn(new QPushButton(i18n("Commit")))
41     , m_summaryEdit(new QLineEdit)
42     , m_messageEdit(new QTextEdit)
43     , m_inlineError(new KMessageWidget)
44     , m_disabled(false)
45 {
46     QHBoxLayout* _hlayout = new QHBoxLayout();
47     _hlayout->setSpacing(0);
48     _hlayout->addWidget(m_summaryEdit);
49     _hlayout->addWidget(m_commitBtn);
50 
51     QVBoxLayout* _vlayout = new QVBoxLayout(this);
52     _vlayout->setSpacing(2);
53     _vlayout->setMargin(0);
54     _vlayout->addWidget(m_inlineError);
55     _vlayout->addLayout(_hlayout);
56     _vlayout->addWidget(m_messageEdit);
57 
58     m_inlineError->setHidden(true);
59     m_inlineError->setMessageType(KMessageWidget::Error);
60     m_inlineError->setCloseButtonVisible(true);
61     m_inlineError->setWordWrap(true);
62 
63     auto monospace = QFontDatabase::systemFont(QFontDatabase::FixedFont);
64     monospace.setStyleHint(QFont::TypeWriter);
65     m_messageEdit->setPlaceholderText(i18n("Extended commit description"));
66     m_messageEdit->setMinimumHeight(30);
67     m_messageEdit->setFont(monospace);
68     m_messageEdit->setWordWrapMode(QTextOption::WordWrap);
69     m_messageEdit->setLineWrapMode(QTextEdit::FixedColumnWidth);
70     m_messageEdit->setLineWrapColumnOrWidth(80);
71 
72     QAction* summaryLen = new QAction(this);
73     m_summaryEdit->setPlaceholderText(i18n("Commit summary"));
74     m_summaryEdit->addAction(summaryLen, QLineEdit::TrailingPosition);
75     m_summaryEdit->setFont(monospace);
76 
77     auto colors = KColorScheme();
78 
79     summaryLen->setIcon(textIcon(QString::number(0), colors.foreground(KColorScheme::NormalText).color(),
80                                  colors.background(KColorScheme::NormalBackground).color()));
81     connect(m_summaryEdit, &QLineEdit::textChanged, this, [=] {
82         int sz = m_summaryEdit->text().size();
83 
84         // Disable the commit button if the message is empty
85         if (sz == 0) {
86             m_commitBtn->setDisabled(true);
87             if (!m_disabled)
88                 m_commitBtn->setToolTip(i18n("To commit changes, please write a commit message first"));
89         } else if (!m_disabled) {
90             m_commitBtn->setToolTip(
91                 i18n("Commit changes to <b>%1</b> on branch <b>%2</b>", m_projectName, m_branchName));
92             m_commitBtn->setDisabled(false);
93         }
94 
95         // Determine the summary background color based on the length
96         QBrush fg, bg;
97 
98         if (sz < 65) {
99             bg = colors.background(KColorScheme::NormalBackground);
100             fg = colors.foreground(KColorScheme::NormalText);
101         } else if (sz < 73) {
102            bg = colors.background(KColorScheme::NormalBackground);
103            fg = colors.foreground(KColorScheme::NormalText);
104         } else if (sz < 79) {
105             bg = colors.background(KColorScheme::NeutralBackground);
106             fg = colors.foreground(KColorScheme::NeutralText);
107         } else {
108             bg = colors.background(KColorScheme::NegativeBackground);
109             fg = colors.foreground(KColorScheme::NegativeText);
110         }
111 
112         QPalette palette = QGuiApplication::palette();
113         palette.setBrush(QPalette::Base, bg);
114         palette.setBrush(QPalette::Text, fg);
115         m_summaryEdit->setPalette(palette);
116         summaryLen->setIcon(textIcon(QString::number(sz), fg.color(), bg.color()));
117     });
118 
119     connect(m_commitBtn, &QPushButton::clicked, this, &SimpleCommitForm::committed);
120     setLayout(_vlayout);
121 
122     m_commitBtn->setToolTip(i18n("To commit changes, please select a project first"));
123     disable();
124 }
125 
extendedDescription(int wrapAtColumn) const126 QString SimpleCommitForm::extendedDescription(int wrapAtColumn) const
127 {
128     if (wrapAtColumn <= 0)
129         return m_messageEdit->toPlainText();
130     int currentLineLen = 0;
131     QString ret;
132     for(const auto c: m_messageEdit->toPlainText()) {
133         if (c == QLatin1Char('\n')) {
134             ret += QLatin1Char('\n');
135             currentLineLen = 0;
136         } else if (currentLineLen > wrapAtColumn && c.isSpace()) {
137             ret += QLatin1Char('\n');
138             currentLineLen = 0;
139         } else {
140             ret += c;
141             currentLineLen++;
142         }
143     }
144     return ret;
145 }
146 
setExtendedDescription(const QString & txt)147 void SimpleCommitForm::setExtendedDescription(const QString& txt)
148 {
149     m_messageEdit->setText(txt);
150 }
summary() const151 QString SimpleCommitForm::summary() const
152 {
153     return m_summaryEdit->text();
154 }
setSummary(const QString & txt)155 void SimpleCommitForm::setSummary(const QString& txt)
156 {
157     m_summaryEdit->setText(txt);
158 }
disableCommitButton()159 void SimpleCommitForm::disableCommitButton()
160 {
161     m_disabled = true;
162     m_commitBtn->setDisabled(true);
163     m_commitBtn->setToolTip(i18n("Please stage some changes before committing."));
164 }
enableCommitButton()165 void SimpleCommitForm::enableCommitButton()
166 {
167     m_disabled = false;
168     if (m_summaryEdit->text().size() > 0) {
169         m_commitBtn->setToolTip(i18n("Commit changes to <b>%1</b> on branch <b>%2</b>", m_projectName, m_branchName));
170         m_commitBtn->setDisabled(false);
171     } else {
172         m_commitBtn->setToolTip(i18n("To commit changes, please write a commit message first"));
173     }
174 }
175 
disable()176 void SimpleCommitForm::disable()
177 {
178     m_commitBtn->setDisabled(true);
179     m_summaryEdit->setDisabled(true);
180     m_messageEdit->setDisabled(true);
181 }
182 
enable()183 void SimpleCommitForm::enable()
184 {
185     if (! m_disabled)
186         enableCommitButton();
187     m_summaryEdit->setDisabled(false);
188     m_messageEdit->setDisabled(false);
189 }
190 
191 
192 
clear()193 void SimpleCommitForm::clear()
194 {
195     m_summaryEdit->clear();
196     m_messageEdit->clear();
197     clearError();
198 }
199 
200 
setBranchName(const QString & branchName)201 void SimpleCommitForm::setBranchName(const QString& branchName)
202 {
203     m_branchName = branchName;
204 }
205 
setProjectName(const QString & projName)206 void SimpleCommitForm::setProjectName(const QString& projName)
207 {
208     m_projectName = projName;
209 }
210 
showError(const QString & error)211 void SimpleCommitForm::showError(const QString& error)
212 {
213     m_inlineError->setText(error);
214     m_inlineError->animatedShow();
215 }
216 
clearError()217 void SimpleCommitForm::clearError()
218 {
219     if (!m_inlineError->isHidden() && !m_inlineError->isHideAnimationRunning()) {
220         m_inlineError->animatedHide();
221     }
222 }
223