diff --git a/icons/trigger-scrolling.svg b/icons/trigger-scrolling.svg new file mode 100644 index 00000000..730aa729 --- /dev/null +++ b/icons/trigger-scrolling.svg @@ -0,0 +1,16 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/pulseview.qrc b/pulseview.qrc index 9acd3fac..76c6ffa0 100644 --- a/pulseview.qrc +++ b/pulseview.qrc @@ -35,6 +35,7 @@ icons/trigger-marker-rising.svg icons/trigger-none.svg icons/trigger-rising.svg + icons/trigger-scrolling.svg icons/view-displaymode-last_complete_segment.svg icons/view-displaymode-last_segment.svg icons/view-displaymode-single_segment.svg diff --git a/pv/mainwindow.hpp b/pv/mainwindow.hpp index aa325e73..bcd787f2 100644 --- a/pv/mainwindow.hpp +++ b/pv/mainwindow.hpp @@ -179,6 +179,7 @@ private Q_SLOTS: QIcon icon_grey_; QShortcut *view_sticky_scrolling_shortcut_; + QShortcut *view_trigger_scrolling_shortcut_; QShortcut *view_show_sampling_points_shortcut_; QShortcut *view_show_analog_minor_grid_shortcut_; QShortcut *view_colored_bg_shortcut_; diff --git a/pv/views/trace/standardbar.cpp b/pv/views/trace/standardbar.cpp index 22697506..7cff4bfb 100644 --- a/pv/views/trace/standardbar.cpp +++ b/pv/views/trace/standardbar.cpp @@ -41,6 +41,7 @@ StandardBar::StandardBar(Session &session, QWidget *parent, action_view_zoom_in_(new QAction(this)), action_view_zoom_out_(new QAction(this)), action_view_zoom_fit_(new QAction(this)), + action_view_trigger_scrolling_(new QAction(this)), action_view_show_cursors_(new QAction(this)), segment_display_mode_selector_(new QToolButton(this)), action_sdm_last_(new QAction(this)), @@ -74,6 +75,14 @@ StandardBar::StandardBar(Session &session, QWidget *parent, connect(action_view_zoom_fit_, SIGNAL(triggered(bool)), this, SLOT(on_actionViewZoomFit_triggered(bool))); + action_view_trigger_scrolling_->setCheckable(true); + action_view_trigger_scrolling_->setText(tr("Scroll to &Trigger")); + action_view_trigger_scrolling_->setIcon(QIcon::fromTheme("trigger-scrolling", + QIcon(":/icons/trigger-scrolling.svg"))); + action_view_trigger_scrolling_->setShortcut(QKeySequence(Qt::Key_T)); + connect(action_view_trigger_scrolling_, SIGNAL(triggered(bool)), + this, SLOT(on_actionViewScrollToTrigger_triggered(bool))); + action_view_show_cursors_->setCheckable(true); action_view_show_cursors_->setIcon(QIcon(":/icons/show-cursors.svg")); action_view_show_cursors_->setShortcut(QKeySequence(Qt::Key_C)); @@ -125,6 +134,9 @@ StandardBar::StandardBar(Session &session, QWidget *parent, connect(view_, SIGNAL(always_zoom_to_fit_changed(bool)), this, SLOT(on_always_zoom_to_fit_changed(bool))); + connect(view_, SIGNAL(trigger_scrolling_changed(bool)), + this, SLOT(on_trigger_scrolling_changed(bool))); + connect(view_, SIGNAL(cursor_state_changed(bool)), this, SLOT(on_cursor_state_changed(bool))); @@ -143,6 +155,7 @@ void StandardBar::add_toolbar_widgets() addAction(action_view_zoom_in_); addAction(action_view_zoom_out_); addAction(action_view_zoom_fit_); + addAction(action_view_trigger_scrolling_); addSeparator(); addAction(action_view_show_cursors_); multi_segment_actions_.push_back(addSeparator()); @@ -198,6 +211,11 @@ void StandardBar::on_actionViewZoomFit_triggered(bool checked) view_->zoom_fit(checked); } +void StandardBar::on_actionViewScrollToTrigger_triggered(bool checked) +{ + view_->trigger_scrolling(checked); +} + void StandardBar::on_actionViewShowCursors_triggered() { const bool show = action_view_show_cursors_->isChecked(); @@ -228,6 +246,11 @@ void StandardBar::on_always_zoom_to_fit_changed(bool state) action_view_zoom_fit_->setChecked(state); } +void StandardBar::on_trigger_scrolling_changed(bool state) +{ + action_view_trigger_scrolling_->setChecked(state); +} + void StandardBar::on_new_segment(int new_segment_id) { if (new_segment_id > 0) { diff --git a/pv/views/trace/standardbar.hpp b/pv/views/trace/standardbar.hpp index 9c27f43e..4a20d759 100644 --- a/pv/views/trace/standardbar.hpp +++ b/pv/views/trace/standardbar.hpp @@ -72,6 +72,7 @@ class StandardBar : public QToolBar QAction *const action_view_zoom_in_; QAction *const action_view_zoom_out_; QAction *const action_view_zoom_fit_; + QAction *const action_view_trigger_scrolling_; QAction *const action_view_show_cursors_; QToolButton *segment_display_mode_selector_; @@ -90,6 +91,7 @@ protected Q_SLOTS: void on_actionViewZoomOut_triggered(); void on_actionViewZoomFit_triggered(bool checked); + void on_actionViewScrollToTrigger_triggered(bool checked); void on_actionViewShowCursors_triggered(); void on_cursor_state_changed(bool show); @@ -99,6 +101,7 @@ protected Q_SLOTS: void on_actionSDMSingle_triggered(); void on_always_zoom_to_fit_changed(bool state); + void on_trigger_scrolling_changed(bool state); void on_new_segment(int new_segment_id); void on_segment_changed(int segment_id); diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index f2a3335e..02d79488 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -233,6 +233,7 @@ void View::reset_view_state() updating_scroll_ = false; settings_restored_ = false; always_zoom_to_fit_ = false; + trigger_scrolling_ = false; tick_period_ = 0; tick_prefix_ = pv::util::SIPrefix::yocto; tick_precision_ = 0; @@ -709,9 +710,20 @@ void View::zoom_fit(bool gui_state) set_scale_offset(scale.convert_to(), extents.first); } +void View::trigger_scrolling(bool gui_state) +{ + trigger_scrolling_ = gui_state; + trigger_scrolling_changed(gui_state); + + // Try to update view right away + if (trigger_scrolling_) { + scroll_to_trigger(); + } +} + void View::set_scale_offset(double scale, const Timestamp& offset) { - // Disable sticky scrolling / always zoom to fit when acquisition runs + // Disable sticky scrolling / always zoom to fit / trigger scrolling when acquisition runs // and user drags the viewport if ((scale_ == scale) && (offset_ != offset) && (session_.get_capture_state() == Session::Running)) { @@ -725,6 +737,11 @@ void View::set_scale_offset(double scale, const Timestamp& offset) always_zoom_to_fit_ = false; always_zoom_to_fit_changed(false); } + + if (trigger_scrolling_) { + trigger_scrolling_ = false; + trigger_scrolling_changed(false); + } } set_scale(scale); @@ -1226,6 +1243,39 @@ void View::set_scroll_default() set_v_offset(extents.first); } +void View::scroll_to_trigger() { + const pair extents = get_time_extents(); + + const QSize areaSize = viewport_->size(); + const Timestamp view_time_width = areaSize.width() * scale_; + const auto trigger_time_max = extents.second - view_time_width; + // Jump back if user is scrolled to the right for some reason + const auto trigger_time_min = offset_ > trigger_time_max ? view_time_width : offset_ + view_time_width; + + // Intuitively the markers would be sorted, + // but I'm not sure the threads guarantees that, + // either way, with this kind of dumb search it doesn't matter too much + // as it will still find some appropriate event to jump to + for (auto it = trigger_markers_.rbegin(); it != trigger_markers_.rend(); it++) + { + const auto marker = (*it); + if (marker->time() > trigger_time_max) + { + continue; + } + + if (marker->time() > trigger_time_min) + { + set_zero_position(marker->time()); + set_offset(marker->time(), true); + ruler_->update(); + } + break; + } + + // TODO actually disable triggers until view is filled +} + void View::determine_if_header_was_shrunk() { const int header_pane_width = @@ -1495,12 +1545,16 @@ void View::h_scroll_value_changed(int value) if (updating_scroll_) return; - // Disable sticky scrolling when user moves the horizontal scroll bar + // Disable sticky scrolling / trigger scrolling when user moves the horizontal scroll bar // during a running acquisition if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) { sticky_scrolling_ = false; sticky_scrolling_changed(false); } + if (trigger_scrolling_ && (session_.get_capture_state() == Session::Running)) { + trigger_scrolling_ = false; + trigger_scrolling_changed(false); + } const int range = scrollarea_->horizontalScrollBar()->maximum(); if (range < MaxScrollValue) @@ -1815,8 +1869,9 @@ void View::perform_delayed_view_update() length = max(length - areaSize.width(), 0.0); set_offset(scale_ * length); + } else if (trigger_scrolling_) { + scroll_to_trigger(); } - determine_time_unit(); update_scroll(); ruler_->update(); diff --git a/pv/views/trace/view.hpp b/pv/views/trace/view.hpp index a0e6f6bc..5b412a74 100644 --- a/pv/views/trace/view.hpp +++ b/pv/views/trace/view.hpp @@ -247,6 +247,7 @@ class View : public ViewBase, public TraceTreeItemOwner, public GlobalSettingsIn void zoom(double steps, int offset); void zoom_fit(bool gui_state); + void trigger_scrolling(bool gui_state); /** * Sets the scale and offset. @@ -350,6 +351,7 @@ class View : public ViewBase, public TraceTreeItemOwner, public GlobalSettingsIn void scale_changed(); void sticky_scrolling_changed(bool state); + void trigger_scrolling_changed(bool state); void always_zoom_to_fit_changed(bool state); @@ -403,6 +405,8 @@ public Q_SLOTS: void set_scroll_default(); + void scroll_to_trigger(); + void determine_if_header_was_shrunk(); void resize_header_to_fit(); @@ -532,6 +536,7 @@ private Q_SLOTS: bool header_was_shrunk_; bool sticky_scrolling_; + bool trigger_scrolling_; bool colored_bg_; bool always_zoom_to_fit_;