From 5071ea08a72e5e960c3c72b9a41cc0a8f1f58cb8 Mon Sep 17 00:00:00 2001 From: Chris Ahlstrom Date: Wed, 21 Aug 2024 13:40:40 -0400 Subject: [PATCH] Added note-info tooltips. --- NEWS | 6 ++- TODO | 49 ++++++++++---------- libseq66/include/play/sequence.hpp | 13 +++++- libseq66/src/play/sequence.cpp | 25 ++++++++++- seq_qt5/forms/qseqeditframe64.ui | 11 ++++- seq_qt5/include/qseqeditframe64.hpp | 18 ++++++-- seq_qt5/include/qseqroll.hpp | 22 ++++++++- seq_qt5/include/qt5_helpers.hpp | 22 ++++++++- seq_qt5/src/qseqeditframe64.cpp | 69 ++++++++++++++++++++++++++--- seq_qt5/src/qseqroll.cpp | 67 +++++++++++++++++++++++++++- seq_qt5/src/qt5_helpers.cpp | 22 ++++++++- 11 files changed, 278 insertions(+), 46 deletions(-) diff --git a/NEWS b/NEWS index e753ffc2..8cb37d2b 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ NEWS for Seq66 0.99.14 Chris Ahlstrom -2015-07-10 to 2024-08-18 +2015-07-10 to 2024-08-21 # Changelog @@ -14,7 +14,8 @@ Chris Ahlstrom if not playing and not in adding mode. Similar to the fix made to the pattern editor for issue #117. - Added the elliptical progress-box option to Preferences / Display. -- Replaced the --session-tag option with --session. +- In the pattern editor, a tiny unlabelled button toggles the + showing of note information when the mouse hovers over a note. ### Fixed @@ -31,6 +32,7 @@ Chris Ahlstrom - Standardized on "Overdub" for the pattern-window merge functionality. See issue #128. - Updating the PDF documentation based on issue fixes and other fixes. +- Replaced the --session-tag option with --session. ## [0.99.13] - 2024-08-05 diff --git a/TODO b/TODO index 4103749f..e24066b6 100644 --- a/TODO +++ b/TODO @@ -1,29 +1,6 @@ TO DO for Seq66 0.99.14 Chris Ahlstrom -2019-04-13 to 2024-08-15 - -Priority: - - - Fix and test new record/quantize handling, and verify that - MIDI automation of these displays in the pattern editor. - - Make sure metronome buttons etc. still work. Recorded measures - not saved. - - Make sure meta events are never sent via MIDI. What we see is - that is_ex_data() events are skipped. We now allow SysEx to be - sent, but we need a test file for it. Can improve this slightly. - - Add "make install" support to qbuild.sh. In progress. - - When zooming, try to keep the same viewport in view. Got this working - for horizontal seqroll and perfroll, but not for vertical (refactoring - needed). Try to center vertically on notes in the viewport. - - Make it center on notes if not visible. - - Can we distinguish note-insertion from movement snap, to avoid the - "L"-then-snap-then-move samba? - - #97 Seq24 differences - - #102 How can I use mutes? - - #103 One-shot (repetitions != 0) patterns do not play - - #111 Time signature changes does not get saved on .midi file - - #112 Send midi-control-out message when New Pattern is created in GUI - - #115 Accessing Non-Registered Parameter Numbers (NRPNs) possible? +2019-04-13 to 2024-08-21 Mutes: @@ -40,13 +17,14 @@ Playlists: From Testing: + - Add an option fix notes by shortening them a few ticks. - File errors (fixed!), but odd issues (w/qsynth): '/usr/local/share/seq66-0.99/midi/FM/judyblue.mid' plays, but some notes are blipped out. Try with another player. '/usr/local/share/seq66-0.99/midi/FM/longhair.mid' plays just fine mostly, some glitches. - The 'usr' time-color settings do not seem to work. Actually, it does - work, but not with the Qt gtk styling. + work, but not with the Qt gtk styling. Document this issue. - The Carpet song has PPQN = 120 and displays weirdly in pattern editor. Too difficult to fix at this time; converted it to 192. In the calculations module, rigorize default_pulses_per_measure() @@ -56,7 +34,6 @@ From Testing: that the user convert to 192 PPQN or one of the other supported settings, then save, and reopen. Test alternate time signatures. - Ongoing efforts: - Verify setmapper/setmaster for odd set sizes. @@ -64,6 +41,26 @@ Ongoing efforts: left in barrage.midi? The Kepler34 "relative change" feature. This feature is macroed out for now. Perhaps should make it a Ctrl-Click or something to start it in addition to "would select". + - Fix and test new record/quantize handling, and verify that + MIDI automation of these displays in the pattern editor. + - Make sure metronome buttons etc. still work. Recorded measures + not saved. + - Make sure meta events are never sent via MIDI. What we see is + that is_ex_data() events are skipped. We now allow SysEx to be + sent, but we need a test file for it. Can improve this slightly. + - Add "make install" support to qbuild.sh. In progress. + - When zooming, try to keep the same viewport in view. Got this working + for horizontal seqroll and perfroll, but not for vertical (refactoring + needed). Try to center vertically on notes in the viewport. + - Make it center on notes if not visible. + - Can we distinguish note-insertion from movement snap, to avoid the + "L"-then-snap-then-move samba? + - #97 Seq24 differences + - #102 How can I use mutes? + - #103 One-shot (repetitions != 0) patterns do not play + - #111 Time signature changes does not get saved on .midi file + - #112 Send midi-control-out message when New Pattern is created in GUI + - #115 Accessing Non-Registered Parameter Numbers (NRPNs) possible? ISSUES: diff --git a/libseq66/include/play/sequence.hpp b/libseq66/include/play/sequence.hpp index a067216f..dccb94f9 100644 --- a/libseq66/include/play/sequence.hpp +++ b/libseq66/include/play/sequence.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-07-30 - * \updates 2024-01-04 + * \updates 2024-08-20 * \license GNU GPLv2 or above * * The functions add_list_var() and add_long_list() have been replaced by @@ -210,6 +210,9 @@ class sequence /** * A structure that holds note information, used, for example, in * sequence::get_next_note(). + * + * If the note is invalid (as might happen in searches), then the + * note value is (-1). */ class note_info @@ -229,7 +232,7 @@ class sequence note_info () : ni_tick_start (0), ni_tick_finish (0), - ni_note (0), + ni_note (0), /* we could initialize this to (-1) */ ni_velocity (0), ni_selected (false) { @@ -256,6 +259,11 @@ class sequence return ni_note; } + bool valid () const + { + return note() >= 0; + } + int velocity () const { return ni_velocity; @@ -1642,6 +1650,7 @@ class sequence bool append_event (const event & er); void sort_events (); event find_event (const event & e, bool nextmatch = false); + note_info find_note (midipulse tick, int note); bool remove_duplicate_events (midipulse tick, int note = (-1)); void notify_change (bool userchange = true); void notify_trigger (); diff --git a/libseq66/src/play/sequence.cpp b/libseq66/src/play/sequence.cpp index aebd2d1d..388139f8 100644 --- a/libseq66/src/play/sequence.cpp +++ b/libseq66/src/play/sequence.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2015-07-24 - * \updates 2024-01-04 + * \updates 2024-08-20 * \license GNU GPLv2 or above * * The functionality of this class also includes handling some of the @@ -3946,6 +3946,29 @@ sequence::find_event (const event & e, bool nextmatch) return evi != m_events.end() ? *evi : s_null_result ; } +sequence::note_info +sequence::find_note (midipulse tick, int note) +{ + bool found = false; + note_info result; + for (auto cev = cbegin(); ! cend(cev); ++cev) + { + draw status = get_note_info(result, cev); + if (status == draw::linked || status == draw::note_on) + { + found = tick >= result.start() && + tick < result.finish() && result.note() == note; + + if (found || result.start() > tick) + break; + } + } + if (! found) + result.ni_note = (-1); + + return result; +} + bool sequence::remove_duplicate_events (midipulse tick, int note) { diff --git a/seq_qt5/forms/qseqeditframe64.ui b/seq_qt5/forms/qseqeditframe64.ui index 5cb21ca2..480d75ab 100644 --- a/seq_qt5/forms/qseqeditframe64.ui +++ b/seq_qt5/forms/qseqeditframe64.ui @@ -108,7 +108,7 @@ - + 48 @@ -121,11 +121,14 @@ 10 + + Toggle the display of note tool-tips. + - true + false @@ -442,6 +445,7 @@ to note events. [ c for selected notes ] + 75 true @@ -528,6 +532,7 @@ to note events. [ c for selected notes ] + 75 false true @@ -1448,6 +1453,7 @@ or a velocity to force upon input. + 75 true @@ -1490,6 +1496,7 @@ or a velocity to force upon input. + 75 false true diff --git a/seq_qt5/include/qseqeditframe64.hpp b/seq_qt5/include/qseqeditframe64.hpp index 06f4647d..f159ea7d 100644 --- a/seq_qt5/include/qseqeditframe64.hpp +++ b/seq_qt5/include/qseqeditframe64.hpp @@ -27,7 +27,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-06-15 - * \updates 2023-11-23 + * \updates 2024-08-21 * \license GNU GPLv2 or above * */ @@ -143,6 +143,8 @@ class qseqeditframe64 final : ); virtual ~qseqeditframe64 (); + void get_position (int & x, int & y); + void initialize_panels (); void set_editor_mode (sequence::editmode mode); void follow_progress (bool expand = false); @@ -303,6 +305,7 @@ private slots: void transpose_notes (); void transpose_harmonic (); void remap_notes (); + void tooltip_mode (bool ischecked); void note_entry (bool ischecked); /* @@ -395,12 +398,21 @@ private slots: Ui::qseqeditframe64 * ui; /** - * In progress, wanting modify the parent's title bar if the parent - * is a qseqeditex object. + * We want to support holding this frame in a qseqeditex window, to + * be able modify the parent's title bar and get position information. + * is a qseqeditex object. If null, then this frame is embedded in the + * main window. */ qseqeditex * m_qseqeditex_frame; + /** + * This item is not null if this frame is embedded in the main window. + * It is actually the Edit tab widget. + */ + + QWidget * m_edit_tab_widget; + /** * Indicates to compress this window vertically, for use in the Edit tab. */ diff --git a/seq_qt5/include/qseqroll.hpp b/seq_qt5/include/qseqroll.hpp index cd71801e..4a682c4e 100644 --- a/seq_qt5/include/qseqroll.hpp +++ b/seq_qt5/include/qseqroll.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-01-01 - * \updates 2023-12-07 + * \updates 2024-08-21 * \license GNU GPLv2 or above * * We are currently moving toward making this class a base class. @@ -47,6 +47,7 @@ * Forward references */ +class QLabel; class QMessageBox; class QTimer; @@ -138,6 +139,7 @@ class qseqroll final : public QWidget, public qseqbase void set_scale (int scale); void set_background_sequence (bool state, int seq); void analyze_seq_notes (); + void show_note_tooltip (int mx, int my); int note_off_length () const; bool add_painted_note (midipulse tick, int note); bool zoom_key_press (bool shifted, int key); @@ -161,6 +163,11 @@ class qseqroll final : public QWidget, public qseqbase void grow_selected_notes (int dx); #endif + void set_tooltip_mode (bool ischecked) + { + m_show_note_info = ischecked; + } + void move_selected_notes (int dx, int dy); void snap_y (int & y); void start_paste(); @@ -234,6 +241,19 @@ class qseqroll final : public QWidget, public qseqbase int m_key; + /** + * If true (the default is false), this will allow hovering to show + * the values for a note in a tooltip. + */ + + bool m_show_note_info; + + /** + * The label that serves as a tooltip. + */ + + QLabel * m_note_tooltip; + /** * Holds the note length in force for this sequence. Used in the * seq66seqroll module only. diff --git a/seq_qt5/include/qt5_helpers.hpp b/seq_qt5/include/qt5_helpers.hpp index 2045d3c9..d8c45a36 100644 --- a/seq_qt5/include/qt5_helpers.hpp +++ b/seq_qt5/include/qt5_helpers.hpp @@ -28,7 +28,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-03-14 - * \updates 2024-08-16 + * \updates 2024-08-20 * \license GNU GPLv2 or above * */ @@ -36,6 +36,13 @@ #include "ctrl/keymap.hpp" /* seq66::qt_modkey_ordinal() */ #include "ctrl/keystroke.hpp" /* seq66::keystroke wrapper class */ +/* + * Currently disabled because it's nearly impossible to get the absolute + * position of the mouse correct with multiple monitors. + */ + +#undef SHOW_GENERIC_TOOLTIPS + class QAction; class QComboBox; class QIcon; @@ -175,6 +182,19 @@ extern void tooltip_for_filename const std::string & filespec, int duration = -1 ); + +#if defined SHOW_GENERIC_TOOLTIPS + +extern void generic_tooltip +( + QWidget * widget, + const std::string & tiptext, + int x, int y, + int msduration = -1 +); + +#endif + extern bool is_empty (const QLineEdit * lineedit); } // namespace seq66 diff --git a/seq_qt5/src/qseqeditframe64.cpp b/seq_qt5/src/qseqeditframe64.cpp index e6b93b7a..47e096a0 100644 --- a/seq_qt5/src/qseqeditframe64.cpp +++ b/seq_qt5/src/qseqeditframe64.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-06-15 - * \updates 2024-08-16 + * \updates 2024-08-21 * \license GNU GPLv2 or above * * The data pane is the drawing-area below the seqedit's event area, and @@ -270,7 +270,7 @@ s_event_items [] = * * \param parent * Provides the parent window/widget for this container window. Defaults - * to null. Normally a pointer to a qseditex fram or the qsmainwnd's + * to null. Normally a pointer to a qseditex frame or the qsmainwnd's * EditTab is passed in this parameter. If the former, we want to be * able to change it's title. * @@ -290,7 +290,8 @@ qseqeditframe64::qseqeditframe64 qseqframe (p, s, parent), performer::callbacks (p), ui (new Ui::qseqeditframe64), - m_qseqeditex_frame (nullptr), /* TBD */ + m_qseqeditex_frame (nullptr), + m_edit_tab_widget (nullptr), m_short_version (shorter), /* short_version() */ m_is_looping (false), m_lfo_wnd (nullptr), @@ -333,7 +334,18 @@ qseqeditframe64::qseqeditframe64 ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); /* part of issue #4 */ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - if (! shorter) + if (shorter) + { + /* + * Currently this seems to have position 0, 0, which does not work + * for showing note tooltips. + */ + +#if defined SHOW_GENERIC_TOOLTIPS + m_edit_tab_widget = parent; +#endif + } + else m_qseqeditex_frame = dynamic_cast(parent); /* @@ -866,6 +878,18 @@ qseqeditframe64::qseqeditframe64 this, SLOT(v_zoom_out()) ); + /* + * Tool-tip mode + */ + + ui->tooltip_button->setCheckable(true); + ui->tooltip_button->setChecked(false); + connect + ( + ui->tooltip_button, SIGNAL(toggled(bool)), + this, SLOT(tooltip_mode (bool)) + ); + /* * Note-entry mode */ @@ -1419,6 +1443,29 @@ qseqeditframe64::setup_alterations () } } +void +qseqeditframe64::get_position (int & x, int & y) +{ + if (not_nullptr(m_qseqeditex_frame)) + { + x = m_qseqeditex_frame->x(); + y = m_qseqeditex_frame->x(); + } +#if defined SHOW_GENERIC_TOOLTIPS + else if (not_nullptr(m_edit_tab_widget)) + { + x = m_edit_tab_widget->x(); + y = m_edit_tab_widget->y(); + } +#else + else + { + x = 0; + y = 0; + } +#endif +} + /** * Instantiates the various editable areas (panels) of the seqedit * user-interface. Note that m_seqkeys and the other panel pointers are @@ -2763,7 +2810,19 @@ qseqeditframe64::popup_sequence_menu () } /** - * Passes the transpose status to the sequence object. + * Pass the status of showing note tooltips to the seqroll + * object. + */ + +void +qseqeditframe64::tooltip_mode (bool ischecked) +{ + if (not_nullptr(m_seqroll)) + m_seqroll->set_tooltip_mode(ischecked); +} + +/** + * Passes the transpose status to the seqroll object. */ void diff --git a/seq_qt5/src/qseqroll.cpp b/seq_qt5/src/qseqroll.cpp index 50a39a5b..7c69bbed 100644 --- a/seq_qt5/src/qseqroll.cpp +++ b/seq_qt5/src/qseqroll.cpp @@ -25,7 +25,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-01-01 - * \updates 2023-12-07 + * \updates 2024-08-21 * \license GNU GPLv2 or above * * Please see the additional notes for the Gtkmm-2.4 version of this panel, @@ -34,12 +34,14 @@ #include /* QApplication keyboardModifiers() */ #include /* base class for seqedit frame(s) */ +#include /* used as a tool-tip for notes */ #include #include #include #include #include + #include "cfg/settings.hpp" /* seq66::usr().key_height(), etc. */ #include "play/performer.hpp" /* seq66::performer class */ #include "qseqeditframe64.hpp" /* seq66::qseqeditframe64 class */ @@ -101,6 +103,8 @@ qseqroll::qseqroll m_pos (0), m_chord (0), m_key (0), + m_show_note_info (false), + m_note_tooltip (nullptr), m_note_length (p.ppqn() * 4 / 16), m_note_off_margin (2), m_background_sequence (seq::unassigned()), @@ -131,6 +135,11 @@ qseqroll::qseqroll m_font.setPointSize(6); /* 8 is too obtrusive */ set_snap(track().snap()); show(); + +#if 0 + setToolTip("Tempory test"); +#endif + m_timer = qt_timer(this, "qseqroll", 1, SLOT(conditional_update())); } @@ -1315,10 +1324,15 @@ qseqroll::mouseMoveEvent (QMouseEvent * event) midipulse tick; convert_xy(0, current_y(), tick, note); m_seqkeys_wid->preview_key(note); + if (m_show_note_info) + show_note_tooltip(current_x(), current_y()); + if (select_action()) { if (drop_action()) (void) snap_current_x(); + + set_dirty(); } if (painting()) { @@ -1327,8 +1341,8 @@ qseqroll::mouseMoveEvent (QMouseEvent * event) convert_xy(current_x(), current_y(), tick, note); (void) add_painted_note(tick, note); } + set_dirty(); } - set_dirty(); } bool @@ -1964,6 +1978,55 @@ qseqroll::follow_progress () frame64()->follow_progress(); } +/** + * Creates a label to show the note information. We cannot get the + * generic_tooltip() function to work properly. + * + * The odd thing is that the font is bold in the main window, but + * regular in the external window. + */ + +void +qseqroll::show_note_tooltip (int mx, int my) +{ + int note; + midipulse tick; + convert_xy(mx, my, tick, note); + sequence::note_info ni = track().find_note(tick, note); + if (ni.valid()) + { + std::string s = perf().pulses_to_measure_string(ni.start()); + std::string f = perf().pulses_to_measure_string(ni.finish()); + std::string temp = "#"; + temp += std::to_string(ni.note()); + temp += ": "; + temp += s; + temp += "-"; + temp += f; + temp += " Vel "; + temp += std::to_string(ni.velocity()); +#if defined SHOW_GENERIC_TOOLTIPS + generic_tooltip(this, temp, mx, my); +#else + if (not_nullptr(m_note_tooltip)) + delete m_note_tooltip; + + m_note_tooltip = new QLabel(qt(temp), this); + m_note_tooltip->show(); + m_note_tooltip->move(mx, my - note_height() - 3); +#endif + } + else + { +#if defined SHOW_GENERIC_TOOLTIPS + generic_tooltip(this, "", mx, my); +#else + delete m_note_tooltip; + m_note_tooltip = nullptr; +#endif + } +} + } // namespace seq66 /* diff --git a/seq_qt5/src/qt5_helpers.cpp b/seq_qt5/src/qt5_helpers.cpp index 2cc8d781..3d038d8a 100644 --- a/seq_qt5/src/qt5_helpers.cpp +++ b/seq_qt5/src/qt5_helpers.cpp @@ -24,7 +24,7 @@ * \library seq66 application * \author Chris Ahlstrom * \date 2018-03-14 - * \updates 2024-01-09 + * \updates 2024-08-20 * \license GNU GPLv2 or above * * The items provided externally are: @@ -72,6 +72,7 @@ #include #include #include +#include #include "cfg/settings.hpp" /* seq66::rc().home_config_dir...() */ #include "util/filefunctions.hpp" /* seq66 file-name manipulations */ @@ -1046,6 +1047,25 @@ tooltip_for_filename } } +/** + * A generic tooltip, meant for use in canvas such as a piano roll. + */ + +void +generic_tooltip +( + QWidget * widget, + const std::string & tiptext, + int x, int y, + int msduration +) +{ + QPoint p{x, y}; + QString qtip{qt(tiptext)}; + QRect qr{}; + QToolTip::showText(p, qtip, widget, qr, msduration); +} + /** * Makes it easier to check a QLineEdit for empty text [QString::isEmpty() as * opposed to QString::isNull()].