Skip to content

Conversation

he29-net
Copy link
Contributor

This PR contains code for a new LcdFloatSpinBox widget, originally included in #5522.

As the name says, it is an LCD Spin Box that can be used for decimal numbers. It is a wrapper class, which instantiates two LcdSpinBoxes, positions them next to each other and adds a decimal point in between. The whole and fractional part can be individually adjusted by a mouse, allowing easy coarse and fine adjustments.

The goal was to have only a "thin" wrapper and instantiate LcdSpinBoxes to avoid redundancy and large changes to existing code, but in the end some parts had to be re-used in some form from the LcdSpinBox class. So there is a good chance it could be designed better, with even less redundancy, but it would likely require a substantial rewrite of the LcdSpinBox class.

@LmmsBot
Copy link

LmmsBot commented Dec 29, 2020

🤖 Hey, I'm @LmmsBot from github.com/lmms/bot and I made downloads for this pull request, click me to make them magically appear! 🎩

Windows

Linux

macOS

🤖
{"platform_name_to_artifacts": {"Windows": [{"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://12785-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.80%2Bg15944565b-mingw-win32.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/12785?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://12783-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.80%2Bg15944565b-mingw-win64.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/12783?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/w60psoj21ggacmav/artifacts/build/lmms-1.3.0-alpha-msvc2017-win32.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/38069679"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/vg6bg2t46csu4k50/artifacts/build/lmms-1.3.0-alpha-msvc2017-win64.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/38069679"}], "Linux": [{"artifact": {"title": {"title": "(AppImage)", "platform_name": "Linux"}, "link": {"link": "https://12782-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.80%2Bg1594456-linux-x86_64.AppImage"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/12782?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}], "macOS": [{"artifact": {"title": {"title": "", "platform_name": "macOS"}, "link": {"link": "https://12784-15778896-gh.circle-artifacts.com/0/lmms-1.3.0-alpha.1.80%2Bg15944565b-mac10.14.dmg"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/12784?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}]}, "commit_sha": "932b01d043eaa27a98621e3ef776765f70ad9591"}

@PhysSong PhysSong requested a review from DomClark December 29, 2020 07:07
Copy link
Contributor

@IanCaio IanCaio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for some code style to be fixed (mostly code block spacing, parenthesis spacing and a if without explicit blocks) and one line that could reuse a variable it looks good to me. I've to look at the paint events with more care, since I don't yet understand them completely. No obvious problems were noticed on a first review of them though.

I left the changes as suggestions so they can be batch applied if you want!

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
@he29-net
Copy link
Contributor Author

he29-net commented Jan 1, 2021

Thanks for the cleaning pass. A lot of the code was copied from the original LcdSpinBox, and since the clang-format was always going to happen "almost tomorrow" for the last year, I did not bother re-formatting the copied code. :))

As for the remaining suggestions, I posted a question on Discord and I will apply or ignore them depending on what happens with the weird one-liner spacing rule.

EDIT: Conventions clarified, suggestions applied.

(adding oneliner spaces)

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Copy link
Member

@DomClark DomClark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, LcdFloatSpinBox is mostly copied from LcdSpinBox, so a lot of these comments apply to that class too. Those comment could be considered out of scope, since the issues referred to aren't introduced by this PR, and could be fixed at the same time as the original class.

void setLabel(const QString &label) { m_label = label; }

public slots:
virtual void update();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this have a name other than update? The base class function isn't virtual, so this function shadows it, not overrides it. I don't know how this interacts with the signal-slot mechanism, but it may not behave as the implementation/callers intended. Also, it needn't be marked virtual, as it doesn't look like it's intended to be overridden.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not even think of any of this, I just copied it from LcdSpinBox and assumed it will work the same. I would guess it still needs to be called update() since that is what Qt calls when it plans to redraw a widget?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since QWidget::update() is non-virtual, the name of this function doesn't matter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it doesn't matter, but I think the name is misleading, shadowing QWidget::update with something different. Also, slot lookups are done using strings, not vtables, so it's still not clear how this interacts with the signal-slot mechanism. I'll investigate it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, I was answering to @he29-net's question.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried to rename it and the display GUI stopped updating. So Qt (or LMMS, not sure who calls the global update() on all widgets) definitely expects the slot to be named update().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this, see the code at https://gist.github.com/DomClark/fc976514c6e1b083d9bce0ab777eefad. It appears slots have virtual behaviour when connected using SIGNAL/SLOT macros, and non-virtual behaviour when using member function pointer connections. This inconsistency, along with the fact that it doesn't work with the modern connection style, suggests to me that it's not a good idea to override slots by name. I'm fine with leaving it as is for now, since the existing LcdSpinBox code does the same thing, but I think we should clean this up in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That inconsistency certainly looks like an invitation for some confusing bugs, interesting..

I wonder what is the intended workflow then? I would expect that a derived widget implements its own update(), so that it can react to any changes specific to the modified version. And as a part of its update() it would call update() of the base class to finish the rest. So the non-virtual behavior should be almost always unwanted, right?

But if update()s are called by Qt on some occasions, wouldn't choosing a different "update" slot name on a derived class mean that it simply won't be called when needed? Or do we only call / signal update()s explicitly from our code?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update() isn't supposed actually update anything itself. Its purpose is to schedule a repaint of the widget in the near future, once control returns to the event loop. If virtual behaviour were intended, it would be a virtual function.

It looks like the code there is to set the LCD display to the current value of the model, in which case it should run in response to changes in the model. The appropriate places for this are in LcdFloatSpinBox::modelChanged (called when a new model is viewed by the spin box) and in response to the Model::dataChanged signal (emitted when the model's value changes).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to @DomClark's comment, ModelView::doConnections connects Model::dataChanged and Model::propertiesChanged to SLOT(update()) of ModelView::m_widget. This means at least one of them should be satisfied:

  • paintEvent() of the model reads the value of the model,
  • The view monitors changes in its model
  • The view implements its own update()

LcdFloatSpinBox(and LcdSpinBox, too) is using the third approach. If this approach is not recommended, we should move to other approaches.
However, this looks out of scope of this PR.

Copy link
Contributor

@IanCaio IanCaio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the code looks good, but there are some things that I think are worth changing, besides the update method name that @DomClark was concerned about.

I also left two comments about things I had doubt about (use of explicit and right margin drawing).

void setLabel(const QString &label) { m_label = label; }

public slots:
virtual void update();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could call it updateDisplay as it's basically what the method is doing.

Comment on lines +196 to +202
if (!m_seamlessLeft && !m_seamlessRight)
{
QStyleOptionFrame opt;
opt.initFrom(this);
opt.state = QStyle::State_Sunken;
opt.rect = QRect(0, 0, m_cellWidth * m_numDigits + (margin + m_marginWidth) * 2 - 1,
m_cellHeight + (margin * 2));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a TODO comment for futurely moving the border drawing to the LcdSpinBox class later. It's inconsistent that on regular spin boxes the border is drawn on the LcdWidget class and on the float spin boxes it's drawn on the LcdFloatSpinBox paint event.

If it's possible to move this to the LcdSpinBox class with no major issues in this PR, I'd even think better to just deal with it now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I read it correctly, but wouldn't that mean that all "vanilla" LcdWidgets would have no borders?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I just meant that on the regular LCD Widget, the borders are being drawn on the LcdWidget class (LcdWidget::paintEvent, where this comment was made) but on the Float LCD Widget they are being drawn on the LcdFloatSpinBox class (LcdFloatSpinBox::paintEvent). As far as I understood, LcdWidget is the LCD number while the LcdSpinBox is the widget itself, that contains the LCD number. LcdFloatSpinBox for example has 2 LcdWidgets.

I was just pointing out it feels inconsistent to draw borders on the LcdWidgets on the regular spin box and on the LcdFloatSpinBox for the float spin box. We probably should do the border drawing on the spin box classes (both LcdSpinBox and LcdFloatSpinBox).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should do the border drawing on the spin box classes (both LcdSpinBox and LcdFloatSpinBox).

But that would mean LcdWidget is left with no border-drawing code, hence my worry that LcdWidgets will not have borders if we do that. They are currently used in the FX mixer (where a nuber is needed, but you cannot change it), so there would be no borders around the green numbers on the FX mixer. Either that, or I'm getting really confused. :D

LcdFloatSpinBox does border drawing simply because it needs to visually join two widgets that were not originally designed to be joined. That's also why there are the new "seamless" parameters in LcdWidget. It all feels a bit like a hack, all the three LCD widgets could be probably rewritten in some way to make it cleaner, but I felt that is already way out of scope of the original Microtuner (I generally try to avoid touching existing code when not necessary, not to mention big rewrites like this).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't realize that LcdWidget was used by other widgets besides the Spin Box. Well, in that case it's probably a big rewrite to move the border to the outer widgets: Even though it makes sense it's out of the scope of this PR. I'm not sure it's worth it adding a TODO comment for the future, but you can ignore this comment then!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could a comment be added at the beginning about the border drawing, just for future reference? Something like:

// When either the right or left is seamless we are in a LcdFloatSpinBox
// and the border drawing will be done by the float spin box class instead
// TODO: Possibly move the frame drawing to the outer widget classes
// (i.e.: Spin boxes) for consistency

I know this sounds unimportant, but if in the future someone decides to change the frame drawing and isn't aware of that, the frames of regular spin boxes and float spin boxes won't match. The TODO also serves as a reference for possibly coming up with an alternative in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment explaining why is the border drawing skipped. I left out the "TODO", because I still do not understand where do you propose to "move" the code. If it is moved, it is no longer there, which breaks LcdWidget borders in the mixer. So we can only copy it to LcdSpinBox, which does not make sense, it only adds more duplicate code that needs to be maintained.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, maybe someone else can step in to give an opinion too, I could be wrong after all. For me it feels like bad design that a widget contains another one (SpinBox contains LcdWidgets) but in some cases the inner widget (LcdWidget) draws the frame and in other cases the outer widget (Float Spinbox) draws the frame. In a ideal scenario either the inner OR the outer widget should be responsible by the frame. That's all I was suggesting, that in the future we move the drawing of the frame to the outer widgets (LcdFloatSpinBox, LcdSpinBox, FxLineLcdSpinBox, etc) or that we come up with a design where we don't have it being drawn in different places.

I understand this is out of the scope of this PR (and obviously not its fault by any means), I just thought about adding a TODO comment so we could remember about maybe fixing it in the future. Just to let you know, this is by no means holding my approval (as I mentioned in Discord, I'm mostly waiting on @DomClark regarding the update method name, the aside the PR LGTM).

Copy link
Member

@DomClark DomClark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with this if @IanCaio is.

Copy link
Contributor

@IanCaio IanCaio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM too!

@IanCaio IanCaio merged commit 17ea616 into LMMS:master Mar 15, 2021
IanCaio added a commit to IanCaio/lmms that referenced this pull request Mar 28, 2021
* Add changes extracted from microtonal PR

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Apply suggestions from code review

(adding oneliner spaces)

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Update src/gui/widgets/LcdFloatSpinBox.cpp

Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>

* Implement suggestions from review

* Reorder constructor parameters, remove old code for cursor movement and hiding from Lcd*SpinBoxes

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Fix right margin width in seamless mode

* Add explanation to border drawing code

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
devnexen pushed a commit to devnexen/lmms that referenced this pull request Apr 10, 2021
* Add changes extracted from microtonal PR

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Apply suggestions from code review

(adding oneliner spaces)

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Update src/gui/widgets/LcdFloatSpinBox.cpp

Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>

* Implement suggestions from review

* Reorder constructor parameters, remove old code for cursor movement and hiding from Lcd*SpinBoxes

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Fix right margin width in seamless mode

* Add explanation to border drawing code

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
sdasda7777 pushed a commit to sdasda7777/lmms that referenced this pull request Jun 28, 2022
* Add changes extracted from microtonal PR

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Apply suggestions from code review

(adding oneliner spaces)

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Update src/gui/widgets/LcdFloatSpinBox.cpp

Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>

* Implement suggestions from review

* Reorder constructor parameters, remove old code for cursor movement and hiding from Lcd*SpinBoxes

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Fix right margin width in seamless mode

* Add explanation to border drawing code

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants