2 * Copyright (c) 2021, S. Gilles <sgilles@sgilles.net>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <QCloseEvent>
22 #include <QSpacerItem>
23 #include <QtWidgets/QScrollBar>
26 * Thin wrapper around QMainWindow with just enough logic to save window
29 class ThisMainWindow
: public QMainWindow
{
31 ThisMainWindow(Session
*the_session
, QWidget
*parent
) :
34 session
= the_session
;
39 closeEvent(QCloseEvent
*event
) override
42 session
->update_cards_on_disk();
51 settings
.beginGroup("MainWindow");
52 resize(settings
.value("size", QSize(400, 300)).toSize());
53 move(settings
.value("pos", QPoint(200, 200)).toPoint());
58 settings_write() const
62 settings
.beginGroup("MainWindow");
63 settings
.setValue("size", size());
64 settings
.setValue("pos", pos());
73 * Slightly modified QScrollArea to only allow vertical scrolling, and
74 * Mostly from https://forum.qt.io/topic/13374/solved-qscrollarea-vertical-scroll-only
77 class QVerticalScrollArea
: public QScrollArea
{
79 QVerticalScrollArea(QWidget
*parent
= 0) :
82 setFrameStyle(QFrame::NoFrame
);
83 setWidgetResizable(true);
84 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
85 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
89 eventFilter(QObject
*o
, QEvent
*e
)
93 e
->type() == QEvent::Resize
) {
94 setMinimumWidth(widget()->minimumSizeHint().width() + verticalScrollBar()->width());
97 return QScrollArea::eventFilter(o
, e
);
102 UI::UI(Session
*the_session
, QWidget
*parent
)
104 session
= the_session
;
106 /* Set up QT stuff */
107 q_main_window
= new ThisMainWindow(session
, parent
);
109 if (q_main_window
->objectName().isEmpty()) {
110 q_main_window
->setObjectName(QString::fromUtf8("sispare-qt"));
113 q_main_window
->setWindowTitle(QString::fromUtf8("Sispare"));
116 action_exit
= new QAction("E&xit", q_main_window
);
117 action_exit
->setObjectName(QString::fromUtf8("action_exit"));
118 QObject::connect(action_exit
, &QAction::triggered
, [this](){
119 q_main_window
->close();
125 +--------------------------------------------------+
127 |-------|------------------------------------------|
134 |--------------------------------------------------|
136 +--------------------------------------------------+
138 T1: Display of the A/B side for current card
139 B1-3: Easy/Hard/Fail buttons
141 B</<: Forward/Back buttons
142 T2: Current progress indicator
143 T3: If we've already marked this card as something
145 central_widget
= new QWidget(q_main_window
);
146 central_widget
->setObjectName(QString::fromUtf8("central_widget"));
148 /* Holds everything */
149 box_layout_0
= new QVBoxLayout();
150 box_layout_0
->setObjectName(QString::fromUtf8("box_layout_0"));
151 box_layout_0
->setSizeConstraint(QLayout::SetMinimumSize
);
153 /* Holds T1, T3, B1-3, and BF */
154 box_layout_top
= new QHBoxLayout();
155 box_layout_top
->setObjectName(QString::fromUtf8("box_layout_top"));
156 box_layout_top
->setSizeConstraint(QLayout::SetMinimumSize
);
158 /* Holds B<, T2, B> */
159 box_layout_bottom
= new QHBoxLayout();
160 box_layout_bottom
->setObjectName(QString::fromUtf8("box_layout_bottom"));
162 /* Holds B1-3 and BF */
163 box_layout_top_left
= new QVBoxLayout();
164 box_layout_top_left
->setObjectName(QString::fromUtf8("box_layout_top_left"));
166 /* Holds T3 and T1 */
167 box_layout_top_main
= new QVBoxLayout();
168 box_layout_top_main
->setObjectName(QString::fromUtf8("box_layout_top_main"));
169 box_layout_top_main
->setSizeConstraint(QLayout::SetMinimumSize
);
172 status_previously_seen
= new QLabel();
173 status_previously_seen
->setObjectName(QString::fromUtf8("status_previously_seen"));
174 status_previously_seen
->setAlignment(Qt::AlignHCenter
);
175 status_previously_seen
->setText(QString::fromUtf8("(already marked as FAILED)"));
178 scrollable_main_card
= new QVerticalScrollArea();
179 scrollable_main_card
->setObjectName(QString::fromUtf8("scrollable_main_card"));
180 scrollable_main_card
->setAlignment(Qt::AlignHCenter
);
181 main_card
= new QLabel();
182 main_card
->setObjectName(QString::fromUtf8("main_card"));
183 main_card
->setSizePolicy(QSizePolicy::Preferred
, QSizePolicy::MinimumExpanding
);
184 main_card
->setWordWrap(true);
185 main_card
->setTextInteractionFlags(Qt::TextSelectableByMouse
);
186 main_card
->setAlignment(Qt::AlignCenter
);
189 remembered
= new QLabel();
190 remembered
->setObjectName(QString::fromUtf8("remembered"));
191 remembered
->setText(QString::fromUtf8("Remembered?"));
194 button_easy
= new QPushButton();
195 button_easy
->setObjectName(QString::fromUtf8("button_easy"));
196 button_easy
->setText(QString::fromUtf8("&Yes"));
197 QObject::connect(button_easy
, &QPushButton::clicked
, [this](){
198 session
->mark_current_card_as(Review_status::Pass_easy
);
203 button_hard
= new QPushButton();
204 button_hard
->setObjectName(QString::fromUtf8("button_hard"));
205 button_hard
->setText(QString::fromUtf8("&Hard"));
206 QObject::connect(button_hard
, &QPushButton::clicked
, [this](){
207 session
->mark_current_card_as(Review_status::Pass_hard
);
212 button_fail
= new QPushButton();
213 button_fail
->setObjectName(QString::fromUtf8("button_fail"));
214 button_fail
->setText(QString::fromUtf8("&No"));
215 QObject::connect(button_fail
, &QPushButton::clicked
, [this](){
216 session
->mark_current_card_as(Review_status::Fail
);
221 button_flip
= new QPushButton();
222 button_flip
->setObjectName(QString::fromUtf8("button_flip"));
223 button_flip
->setText(QString::fromUtf8("Flip &Over"));
224 QObject::connect(button_flip
, &QPushButton::clicked
, [this](){
225 session
->flip_current_card();
230 button_prev
= new QPushButton();
231 button_prev
->setObjectName(QString::fromUtf8("button_prev"));
232 button_prev
->setText(QString::fromUtf8("Back"));
233 QObject::connect(button_prev
, &QPushButton::clicked
, [this](){
234 session
->move_prev_card();
239 button_next
= new QPushButton();
240 button_next
->setObjectName(QString::fromUtf8("button_next"));
241 button_next
->setText(QString::fromUtf8("Next"));
242 QObject::connect(button_next
, &QPushButton::clicked
, [this](){
243 session
->move_next_card();
248 status_progress
= new QLabel();
249 status_progress
->setObjectName(QString::fromUtf8("status_progress"));
251 /* Add everything to everything else */
252 box_layout_top_left
->addSpacing(75);
253 box_layout_top_left
->addWidget(remembered
);
254 box_layout_top_left
->addSpacing(25);
255 box_layout_top_left
->addWidget(button_easy
);
256 box_layout_top_left
->addWidget(button_hard
);
257 box_layout_top_left
->addWidget(button_fail
);
258 box_layout_top_left
->addStretch(0);
259 box_layout_top_left
->addWidget(button_flip
);
260 box_layout_top_left
->addStretch(0);
261 scrollable_main_card
->setWidget(main_card
);
262 box_layout_top_main
->addWidget(status_previously_seen
);
263 box_layout_top_main
->addWidget(scrollable_main_card
);
264 box_layout_bottom
->addSpacing(100);
265 box_layout_bottom
->addWidget(button_prev
);
266 box_layout_bottom
->addStretch(0);
267 box_layout_bottom
->addWidget(status_progress
);
268 box_layout_bottom
->addStretch(0);
269 box_layout_bottom
->addWidget(button_next
);
270 box_layout_bottom
->addSpacing(100);
271 box_layout_top
->addLayout(box_layout_top_left
);
272 box_layout_top
->addSpacing(15);
273 box_layout_top
->addLayout(box_layout_top_main
);
274 box_layout_0
->addLayout(box_layout_top
);
275 box_layout_0
->addLayout(box_layout_bottom
);
276 central_widget
->setLayout(box_layout_0
);
277 q_main_window
->setCentralWidget(central_widget
);
280 menu_bar
= new QMenuBar(q_main_window
);
281 menu_bar
->setObjectName(QString::fromUtf8("menu_bar"));
282 menu_bar
->setGeometry(QRect(0, 0, 243, 21));
283 menu_bar_file
= new QMenu(menu_bar
);
284 menu_bar_file
->setTitle(QString::fromUtf8("&File"));
285 menu_bar_file
->setObjectName(QString::fromUtf8("menu_bar_file"));
286 q_main_window
->setMenuBar(menu_bar
);
287 menu_bar
->addAction(menu_bar_file
->menuAction());
288 menu_bar_file
->addAction(action_exit
);
290 /* Now the UI is all set up. Start the session by showing the first card */
296 /* Deletes all children as well */
297 delete q_main_window
;
303 q_main_window
->show();
307 * Deal with the pretty simple state exposed by Session. Enable/disable
308 * buttons and set text contents as appropriate.
311 UI::sync_with_session()
313 status_previously_seen
->setText(QString::fromStdString(session
->get_previously_seen_string()));
314 button_prev
->setEnabled(session
->have_prev_card());
315 button_next
->setEnabled(session
->have_next_card());
316 status_progress
->setText(QString::fromStdString(session
->get_progress_string()));
318 switch (session
->get_current_action()) {
319 case Currently_doing::Viewing_A
:
320 main_card
->setText(QString::fromStdString(session
->get_current_A_side().value_or("")));
321 main_card
->setAlignment(Qt::AlignCenter
);
322 main_card
->setStyleSheet("#main_card { font-size: 80pt; }");
323 status_progress
->setVisible(true);
324 button_easy
->setEnabled(false);
325 button_hard
->setEnabled(false);
326 button_fail
->setEnabled(false);
327 button_flip
->setEnabled(true);
329 case Currently_doing::Viewing_B
:
330 main_card
->setText(QString::fromStdString(session
->get_current_B_side().value_or("")));
331 main_card
->setAlignment(Qt::AlignJustify
);
332 main_card
->setStyleSheet("#main_card { font-size: 14pt; }");
333 status_progress
->setVisible(true);
334 button_easy
->setEnabled(true);
335 button_hard
->setEnabled(true);
336 button_fail
->setEnabled(true);
337 button_flip
->setEnabled(true);
339 case Currently_doing::Finished
:
340 main_card
->setText(QString::fromStdString(session
->get_statistics()));
341 main_card
->setAlignment(Qt::AlignHCenter
);
342 main_card
->setStyleSheet("#main_card { }");
343 status_progress
->setVisible(false);
344 button_easy
->setEnabled(false);
345 button_hard
->setEnabled(false);
346 button_fail
->setEnabled(false);
347 button_flip
->setEnabled(false);
349 case Currently_doing::Trivial
:
350 main_card
->setText(QString::fromUtf8("No review necessary."));
351 main_card
->setAlignment(Qt::AlignHCenter
);
352 main_card
->setStyleSheet("#main_card { }");
353 status_progress
->setVisible(false);
354 button_easy
->setEnabled(false);
355 button_hard
->setEnabled(false);
356 button_fail
->setEnabled(false);
357 button_flip
->setEnabled(false);