Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library navigation controls #953

Merged
merged 19 commits into from
Jan 9, 2017

Conversation

timrae
Copy link
Contributor

@timrae timrae commented May 22, 2016

Added three new controls that make it a lot easier to use the library encoder on DJ controllers by allowing the following functionality:

  • SelectItemKnob allows the library encoder to scroll through sidebar items when the sidebar has focus, as opposed to SelectTrackKnob which only scans through the library and SelectPlaylist which only scans through the sidebar items.
  • ToggleFocusWidget allows users to toggle the focus between the sidebar and the library.
  • ChooseItem allows the library encoder push button to either expand a sidebar item if sidebar has focus, or load a track into first stopped deck if library has focus. This is intuitive as it's the same behavior as a double click.

@@ -14,6 +14,7 @@ class LibraryView {
virtual ~LibraryView() {};

virtual void onShow() = 0;
virtual bool hasFocus() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't it make sense to provide QWidget::hasFocus() as a default implementation instead of declaring the function as pure virtual and let derived classes decide if they really need to override the default implementation? I see a lot of code duplication in derived classes that are overriding this function, always using the same implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could hasFocus() be const?

Copy link
Contributor Author

@timrae timrae May 22, 2016

Choose a reason for hiding this comment

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

I was originally trying to do what you said, but I don't really know how to do that in C++ (I've only just started learning). LibraryView is an interface that doesn't have knowledge of whether or not the classes that implement it are QWidgets or not so we can't really add it here. Where would I put the default implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could hasFocus() be const?

Yes thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I didn't have a look at the inheritance hierarchy. Then we should
leave it as it is.

On 05/22/2016 03:58 PM, Tim Rae wrote:

In src/library/libraryview.h
#953 (comment):

@@ -14,6 +14,7 @@ class LibraryView {
virtual ~LibraryView() {};

 virtual void onShow() = 0;
  • virtual bool hasFocus() = 0;

I was originally trying to do what you said, but I don't really know how
to do that in C++ (I've only just started learning). LibraryView is an
interface that doesn't have knowledge of whether or not the classes that
implement it are QWidgets or not so we can't add it here. Where would I
put the default implementation?


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
https://github.com/mixxxdj/mixxx/pull/953/files/8491121006d713c19c76f205123f1d3b0ba792d6#r64150309

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK so I'll add const to them all

@timrae timrae force-pushed the library-navigation-controls branch from 8491121 to 71cc630 Compare May 22, 2016 14:31
@timrae
Copy link
Contributor Author

timrae commented May 22, 2016

@uklotzde

Thanks for the review, I've made the two changes you requested

@timrae timrae force-pushed the library-navigation-controls branch 2 times, most recently from df738e3 to a6e10d5 Compare May 22, 2016 14:34
return;
}
// Select track if a LibraryView object has focus, otherwise select sidebar item
if (m_pLibraryWidget->getActiveView()->hasFocus()) {
Copy link
Member

Choose a reason for hiding this comment

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

getActiveView can return null so we should check that the result is not null before dereferencing

@timrae
Copy link
Contributor Author

timrae commented May 22, 2016

@rryan

Sorry I had noticed that earlier and had forgotten to fix it! I added a new commit

@Be-ing
Copy link
Contributor

Be-ing commented May 22, 2016

Thanks for adding these. How do you propose mapping them? SelectItemKnob for turning a browse encoder, ToggleFocusWidget for pressing the encoder, ChooseItem for shift + press encoder?

IMO the "Knob" should be dropped from "SelectItemKnob" considering this should be mapped to encoders, not knobs.

@timrae
Copy link
Contributor Author

timrae commented May 22, 2016

Yeah basically mapped like that, except on the mc4000 there's a separate
button called panel that does the focus change, so would use that instead
of shift + encoder press on my controller.

Re: name. I chose it to be consistent with the existing "SelectTrackKnob"
that everyone maps to the encoder. If everyone thinks it shouldn't have the
"Knob" in the name then how about we deprecate "SelectTrackKnob" and add a
"SelectTrack" control to replace it?
On 23 May 2016 01:03, "Be" notifications@github.com wrote:

How do you propose mapping these? SelectItemKnob for turning a browse
encoder, ToggleFocusWidget for pressing the encoder, ChooseItem for shift +
press encoder?

IMO the "Knob" should be dropped from "SelectItemKnob" considering this
should be mapped to encoders, not knobs.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#953 (comment)

@timrae
Copy link
Contributor Author

timrae commented May 22, 2016

To be honest I don't really like the "Select" in the name either as it's
ambiguous with the double click event. Maybe something like "NextPrevTrack"
and "NextPrevItem" would be more appropriate...?
On 23 May 2016 04:36, "Tim Rae" perceptualchaos2@gmail.com wrote:

Yeah basically mapped like that, except on the mc4000 there's a separate
button called panel that does the focus change, so would use that instead
of shift + encoder press on my controller.

Re: name. I chose it to be consistent with the existing "SelectTrackKnob"
that everyone maps to the encoder. If everyone thinks it shouldn't have the
"Knob" in the name then how about we deprecate "SelectTrackKnob" and add a
"SelectTrack" control to replace it?
On 23 May 2016 01:03, "Be" notifications@github.com wrote:

How do you propose mapping these? SelectItemKnob for turning a browse
encoder, ToggleFocusWidget for pressing the encoder, ChooseItem for shift +
press encoder?

IMO the "Knob" should be dropped from "SelectItemKnob" considering this
should be mapped to encoders, not knobs.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub
#953 (comment)

@daschuer
Copy link
Member

The change of input focus is hard to get in all skins. I have just filed this bug:
https://bugs.launchpad.net/mixxx/+bug/1584613

I am afraid the original interface will be obsolete soon, since we are working on a 3 column layout, that will be hard to control with it. So we are free to introduce a new namespace, though I am happy with the current names.

IMHO the new interface, should also allow the default keyboard behavior on a controller with:
"Up" SelectPrevItem
"Down" SelectNextItem
"Left" move selection left / collapse
"Right" move selection right / expand
"Enter" ChooseItem
"Tab" ToggleFocusWidget

We have also the issue, that the current interface unlikely to the keyboard does not continue to scroll when holding the up/down button on the controller.

@timrae: Do you have time and fun to add the missing controls?

@daschuer
Copy link
Member

@daschuer daschuer mentioned this pull request Aug 14, 2016
The new SelectItemKnob control selects either the next track or the next sidebar item, depending on which widget has focus
The new ToggleFocusWidget toggles the focus between the sidebar and the library by sending a Tab button press to the sidebar widget
The new ChooseItem toggles acts like a double-click, either toggling the expanded state of a sidebar item or loading the selected track into the first stopped deck if a track is selected
@timrae timrae force-pushed the library-navigation-controls branch from 74fa469 to bbb536b Compare January 7, 2017 06:48
@timrae
Copy link
Contributor Author

timrae commented Jan 7, 2017

@daschuer

I've implemented the controls that you requested. I used slightly different names than what we discussed as I think these are more clear. Please re-review the code.

void slotMoveUp(double);
void slotMoveDown(double);
void slotMoveVertical(double);
void slotMoveLeft(double);
void slotMoveRight(double);
void slotMoveHorizontal(double);
void slotMoveFocusForward(double);
void slotMoveFocusBackward(double);
void slotMoveFocus(double);
void slotChooseItem(double v);

The only problem that I've experienced during my testing is that, when mixxx is initially started up, no widget has the focus. I've spent several hours trying to get that working, but the only way I could get it to work is by setting a timer with an arbitrary delay, as in 5a6a90a. The delay is necessary as setFocus() will only be effective when the widget is actually showing.

Does anyone have any better idea to implement this? Also, I'd rather have the main library pane focused instead of the sidebar... Is it supposed to be the widget in wtracktableview.cpp?? Setting focus on that didn't appear to work.

@timrae timrae force-pushed the library-navigation-controls branch 2 times, most recently from 32e5d59 to 5a6a90a Compare January 7, 2017 09:14
@timrae timrae force-pushed the library-navigation-controls branch from 5a6a90a to 89f5cfd Compare January 7, 2017 16:19
void emitKeyEvent(QKeyEvent&& event);

// Controls to navigate vertically within currently focussed widget (up/down buttons)
ControlPushButton* m_pMoveUp = nullptr;
Copy link
Member

Choose a reason for hiding this comment

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

In this case, the Initialisation here is redundant since they are initialized in the constructor again. It should be removed. This code inlines a delegating constructor, in each user code.


General :
You use here a new C++ feature, which is used in Mixxx here the first time.
"Non-static data member initializers"

There are some pros and cons discussed for this feature:
https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/zqB-DySA4V0

In my real live, I us recently C# and it also supports this feature. It allows to spread class initialisation to the whole file which is from my experience not very readable.
In the current Mixxx code you just need to look at the constructor. If we allow to do it in the class definition we brake this rule.

I am not strictly against this feature, but we should come to a conclusion how we will use it in Mixxx in future.
For example it might be nice to use it for inline classes, or If there is would be only an empty constructor with an initialisation list.

Any thoughts?
@rryan @uklotzde

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At my work I use the convention that only the member variables which depend on the input arguments go in the initializer list. If the variable should be initialized with a value which is known at compile time then we always put the value in at the point of declaration in the header file.

This has two advantages:

  1. It requires less code

  2. It is less maintenance work because the order of declaration in the header file in principle should be the same as the order of initialization.

http://stackoverflow.com/questions/1242830/constructor-initialization-list-evaluation-order

Because of this stupid c++ rule it's a good idea to only put the necessary members into the initializer list for your own sanity

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.e the priority order should be:

  1. header file, point of declaration
  2. initializer list
  3. constructor body

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case to follow that rule I guess we should try to move all of the controller variable initializations to the initialization list. I didn't try it yet though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK I added a commit using the rules I mentioned above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is an other thing, that bugs me more: How to atomize the live time handling of the ControlObjects.

Honestly, modern C++ style teaches to feel sick every time you see a "naked new". I don't really understand how the QT object tree is supposed to work... I guess most developers don't.

You previously mentioned some benchmarks you guys did on "smart pointers" vs. the object tree. Is there any code remaining for this? I often hear people make these kind of claims that "smart pointers are slow" but actually std::unique_ptr has almost zero overhead, and inevitably when we look at the "slow" code they're using a different smart pointer or doing something wrong in the first place.

Copy link
Member

Choose a reason for hiding this comment

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

Having the creation of the control and connecting its signals next to each other improves understandability -- that's why most controls are created this way instead of being in the member initialization list. I think at least for ControlProxys we could move to a pattern where we create them as member variables (instead of allocating in the constructor) letting the no-args constructor run, and then call initialize(ConfigKey) alongside the connection calls. It's more complicated for ControlObject derivatives since we pass a lot of configuration through the constructor args.

I think @daschuer's proposed alternative to is to follow the style of most code in Mixxx and not use non-static data member initializers except for the cases he outlined: "For example it might be nice to use it for inline classes, or If there is would be only an empty constructor with an initialisation list."

That sounds reasonable to me. I like using non-static member initialization for classes/structs that are short-and-sweet (e.g. helper structs with no methods). I share the concern that it makes it harder to be sure you've initialized everything and makes you have to check 2 places (or 2 + n for delegating constructors) to figure out the initial value of the class state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having the creation of the control and connecting its signals next to each other improves understandability

How so?

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 think @daschuer's proposed alternative is...

Yes I understood the part related to the non-static member initialization, but I was referring to the initializer list. Can you put the rules into simple words? Is there a style guide on the wiki?

Copy link
Member

Choose a reason for hiding this comment

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

The de-facto rule is "initialize every member in the member initializer list". ControlObjects would be the most common exception. I don't think the Qt object tree bit is relevant to this particular discussion since you can easily create objects that are part of the tree with the member initializer list since you can pass "this" as the parent if you choose.

The style guide is here:
http://www.mixxx.org/wiki/doku.php/coding_guidelines
but it does not say anything beyond the Google C++ style guide about member variable initialization.

@timrae timrae force-pushed the library-navigation-controls branch from 89f5cfd to 522a0e4 Compare January 7, 2017 20:05
@timrae
Copy link
Contributor Author

timrae commented Jan 7, 2017

I had a better idea to solve the setFocus() issue I mentioned above, and force pushed a new commit to replace that.

@daschuer
Copy link
Member

daschuer commented Jan 8, 2017

@timrae
Copy link
Contributor Author

timrae commented Jan 8, 2017

Done: https://www.mixxx.org/wiki/doku.php/coding_guidelines#unique_ptr_stdmove
please review.

Yes! Prefer to QScopedPointer.

Change "to" to "over". I misread it first to mean "Prefer to use QScopedPointer"

If possible, prefer std::unique_ptr in std:: containers over QSharedPointer in Qt containers.

I think it's confusing to sometimes allow QT smart pointers. IMO if we're going to always prefer unique_ptr over QScopedPointer, we should also prefer shared_ptr over QSharedPointer. In which case ho about changing the text above to:

std::unique_ptr in general cannot be used in QT containers, as these require the copy constructor. There are two ways to solve this: A) Convert the QT container to an std:: container (preferred) or B) Use std::shared_ptr inside a Qt container (only in cases where conversion is not feasible).

Site note

Side note ;)

@daschuer
Copy link
Member

daschuer commented Jan 8, 2017

Typos fixed, thanks.

I think it's confusing to sometimes allow QT smart pointers. IMO if we're going to always prefer unique_ptr over QScopedPointer, we should also prefer shared_ptr over QSharedPointer. In which case ho about changing the text above to:

Is there a benefit of switching to shared_ptr?

IMHO we should prefer Qt classes in our Qt code, if there is no reason for other classes.

Due to the move semantic a std:unique_ptr can be used instead of a QScopedPointer, which clarifies the ownership, but if we really have shared ownership, QSharedPointer will work and fits to the surrounding code.

@timrae
Copy link
Contributor Author

timrae commented Jan 8, 2017

Ok, I don't really have a strong preference... I'm just more accustomed to STL classes, but if you only want unique ptr for the move semantics I guess you could say explicitly: prefer unique ptr over QScoped and prefer QShared over shared ptr

@timrae
Copy link
Contributor Author

timrae commented Jan 8, 2017

Honestly though, it feels a bit like "pissing into the wind" trying to use the QT data structures in a C++11 and beyond world that they clearly weren't designed for.

@rryan
Copy link
Member

rryan commented Jan 9, 2017

IMHO we should prefer Qt classes in our Qt code, if there is no reason for other classes.

👍 to continuing to prefer Qt classes as the default, unless there's a good reason otherwise (such as here).

One benefit I see is that Qt uses the same implementation on all platforms, while the C++ standard library implementation changes per-platform. So using Qt classes we can make slightly stronger assumptions about the implementation we're getting for a given class.

One small benefit to using QSharedPointer is that you get little integrations with QObjects (the pointer automatically goes null when a QObject is deleted by another means, which is a nice safeguard against bugs).

std::unique_ptr seems strictly better than QScopedPointer and I think we should continue to replace it everywhere.

@rryan
Copy link
Member

rryan commented Jan 9, 2017

Honestly though, it feels a bit like "pissing into the wind" trying to use the QT data structures in a C++11 and beyond world that they clearly weren't designed for.

Yea, though in Qt5 a lot of containers became movable, and I'm hoping we will get better support for things like emplacement, etc. in the future.

@timrae timrae mentioned this pull request Jan 9, 2017
@timrae
Copy link
Contributor Author

timrae commented Jan 9, 2017

@daschuer

Yes. .. and there is no reason to delay this PR for future decisions.

I guess this is ready to merge then?

@daschuer
Copy link
Member

daschuer commented Jan 9, 2017

Yes, Thank you very much! LGTM

@daschuer daschuer merged commit cb88711 into mixxxdj:master Jan 9, 2017
@sblaisot
Copy link
Member

@timrae
Copy link
Contributor Author

timrae commented Jan 10, 2017 via email

@timrae timrae mentioned this pull request Jan 11, 2017
@timrae timrae deleted the library-navigation-controls branch January 11, 2017 08:59
@timrae
Copy link
Contributor Author

timrae commented Jan 11, 2017

@sblaisot
Done

@Be-ing
Copy link
Contributor

Be-ing commented Jan 19, 2017

Backwards compatibility with the old [Playlist] COs is broken.

@daschuer
Copy link
Member

It looks like we break something:
https://bugs.launchpad.net/mixxx/+bug/1657743
@timrae: Can you have a look?

@timrae
Copy link
Contributor Author

timrae commented Jan 20, 2017 via email

@timrae
Copy link
Contributor Author

timrae commented Jan 20, 2017

#1137

@Be-ing
Copy link
Contributor

Be-ing commented May 14, 2017

Testing these new Controls in master from the Developer Tools menu in Developer Mode doesn't seem to do anything. Are they dependent on #1117 to work?

@timrae
Copy link
Contributor Author

timrae commented May 31, 2017

They shouldn't be, could you please give some simple steps for me to reproduce the issues you are seeing?

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.

6 participants