From cfd19220d542532ba1f06455d333c85ec90aad3a Mon Sep 17 00:00:00 2001 From: Twan van Laarhoven Date: Tue, 5 May 2020 22:36:00 +0200 Subject: [PATCH] Use unique_ptr --- src/gui/control/card_editor.cpp | 130 +++++++++++++++++++------------- src/gui/control/card_editor.hpp | 19 +++-- 2 files changed, 89 insertions(+), 60 deletions(-) diff --git a/src/gui/control/card_editor.cpp b/src/gui/control/card_editor.cpp index 1d0b95c6..e2f579e9 100644 --- a/src/gui/control/card_editor.cpp +++ b/src/gui/control/card_editor.cpp @@ -125,22 +125,16 @@ struct CompareTabOrder { if (as.tab_index < as.tab_index) return true; if (as.tab_index > as.tab_index) return false; // otherwise look at the positions - // TODO: does this actually give a total order? - double vertical_overlap = min(bs.bottom - as.top, as.bottom - bs.top); - double horizontal_overlap = min(bs.right - as.left, as.right - bs.left); - if (vertical_overlap > 0 && vertical_overlap > horizontal_overlap) { - // fields overlap (mostly) vertically - // the fields are almost on the same 'row' - // compare horizontally first - if (as.left < bs.left) return true; // horizontal sorting - if (as.left > bs.left) return false; - if (as.top < bs.top) return true; // vertical sorting - } else { - // compare vertically first - if (as.top < bs.top) return true; // vertical sorting - if (as.top > bs.top) return false; - if (as.left < bs.left) return true; // horizontal sorting - } + // To get a total order, we look at the viewer center. + // Not completely (y,x), because for viewers that are almost at the same y we prefer to sort by x + double ax = 2*as.left + as.right; // bias a bit to the left + double bx = 2*bs.left + bs.right; + double ay = as.top + as.bottom + 0.1*ax; // a bit of x, so that dominates when y is approximately equal + double by = bs.top + bs.bottom + 0.1*bx; + if (ay < by) return true; + if (ay > by) return false; + if (ax < bx) return true; + if (ax > bx) return false; // arbitrary order otherwise return a < b; } @@ -203,25 +197,50 @@ void DataEditor::onInit() { current_viewer = nullptr; current_editor = nullptr; hovered_viewer = nullptr; + viewers_in_search_order.clear(); // hide caret if it is shown wxCaret* caret = GetCaret(); if (caret->IsVisible()) caret->Hide(); } // ----------------------------------------------------------------------------- : Search / replace -bool DataEditor::search(FindInfo& find, bool from_start) { - bool include = from_start; - for (size_t i = 0 ; i < by_tab_index.size() ; ++i) { - ValueViewer& viewer = *by_tab_index[find.forward() ? i : by_tab_index.size() - i - 1]; - if (&viewer == current_viewer) include = true; - if (include && viewer.getField()->editable && viewer.getStyle()->isVisible()) { - ValueEditor* editor = viewer.getEditor(); - if (editor && editor->search(find, from_start || &viewer != current_viewer)) { - return true; // done +template +bool DataEditor::search(Iterator it, Iterator end, FindInfo& find, bool from_start) { + bool include = from_start || current_viewer == nullptr; + for (;it != end; ++it) { + ValueViewer* viewer = *it; + if (viewer == current_viewer) include = true; + if (include && viewer->getField()->editable && viewer->getStyle()->isVisible()) { + ValueEditor* editor = viewer->getEditor(); + if (editor) { + if (editor && editor->search(find, from_start || viewer != current_viewer)) { + selectViewer(viewer); + return true; // done + } } } } - return false; // not done + return false; +} + +vector init_search_order(vector const& viewers) { + vector in_order; + for (auto const& v : viewers) { + if (v->getEditor()) in_order.push_back(v.get()); + } + stable_sort(in_order.begin(), in_order.end(), CompareTabOrder()); + return in_order; +} + +bool DataEditor::search(FindInfo& find, bool from_start) { + if (viewers_in_search_order.empty()) { + viewers_in_search_order = init_search_order(viewers); + } + if (find.forward()) { + return search(viewers_in_search_order.begin(), viewers_in_search_order.end(), find, from_start); + } else { + return search(viewers_in_search_order.rbegin(), viewers_in_search_order.rend(), find, from_start); + } } // ----------------------------------------------------------------------------- : Clipboard & Formatting @@ -261,7 +280,7 @@ void DataEditor::onLeftDown(wxMouseEvent& ev) { ev.Skip(); // for focus CaptureMouse(); // change selection? - selectField(ev, &ValueEditor::onLeftDown); + selectViewer(ev, &ValueEditor::onLeftDown); } void DataEditor::onLeftUp(wxMouseEvent& ev) { if (HasCapture()) ReleaseMouse(); @@ -283,7 +302,7 @@ void DataEditor::onLeftDClick(wxMouseEvent& ev) { void DataEditor::onRightDown(wxMouseEvent& ev) { ev.Skip(); // for context menu // change selection? - selectField(ev, &ValueEditor::onRightDown); + selectViewer(ev, &ValueEditor::onRightDown); } void DataEditor::onMouseWheel(wxMouseEvent& ev) { if (current_editor && current_viewer) { @@ -303,16 +322,10 @@ void DataEditor::onMotion(wxMouseEvent& ev) { if (!HasCapture()) { // find editor under mouse ValueViewer* old_hovered_viewer = hovered_viewer; - FOR_EACH_REVERSE(v,viewers) { // find high z index fields first - RealPoint pos = mousePoint(ev, *v); - if (v->containsPoint(pos) && v->getField()->editable) { - hovered_viewer = v.get(); - break; - } - } + hovered_viewer = mousedOverViewer(ev); if (old_hovered_viewer && hovered_viewer != old_hovered_viewer) { ValueEditor* e = old_hovered_viewer->getEditor(); - RealPoint pos = mousePoint(ev, *hovered_viewer); + RealPoint pos = mousePoint(ev, *old_hovered_viewer); if (e) e->onMouseLeave(pos, ev); if (draw_hover_borders) redraw(*old_hovered_viewer); } @@ -350,15 +363,26 @@ void DataEditor::onMouseLeave(wxMouseEvent& ev) { if (frame) frame->SetStatusText(wxEmptyString); } -void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)) { - // change viewer/editor +bool DataEditor::selectViewer(ValueViewer* v) { + if (!v) return false; + ValueEditor* e = v->getEditor(); + if (!e) return false; ValueEditor* old_editor = current_editor; - selectFieldNoEvents(ev); + current_editor = e; + current_viewer = v; if (old_editor != current_editor) { // selection has changed, send focus events if (old_editor) old_editor->onLoseFocus(); if (current_editor) current_editor->onFocus(); + return true; } + return false; +} + +void DataEditor::selectViewer(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)) { + // change viewer/editor + ValueViewer* viewer = mousedOverViewer(ev); + bool changed = viewer && selectViewer(viewer); // pass event if (current_editor && current_viewer) { RealPoint pos = mousePoint(ev, *current_viewer); @@ -367,31 +391,31 @@ void DataEditor::selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const } } // refresh? - if (old_editor != current_editor) { + if (changed) { // selection has changed, refresh viewers // NOTE: after passing mouse down event, otherwise opening combo box produces flicker onChange(); } } -void DataEditor::selectFieldNoEvents(const wxMouseEvent& ev) { - FOR_EACH_EDITOR_REVERSE { // find high z index fields first - int y; - if (v->getField()->editable && (v->containsPoint(mousePoint(ev,*v)) || - (nativeLook() && (y = ev.GetY() + GetScrollPos(wxVERTICAL)) >= v->getStyle()->top - && y < v->getStyle()->bottom) )) { - current_viewer = v.get(); - current_editor = e; - return; - } - } -} -RealPoint DataEditor::mousePoint(const wxMouseEvent& ev, const ValueViewer& viewer) { +RealPoint DataEditor::mousePoint(const wxMouseEvent& ev, const ValueViewer& viewer) const { Rotation rot = getRotation(); Rotater r(rot,viewer.getRotation()); return rot.trInv(RealPoint(ev.GetX(), ev.GetY())); } +ValueViewer* DataEditor::mousedOverViewer(const wxMouseEvent& ev) const { + FOR_EACH_EDITOR_REVERSE{ // find high z index fields first + int y; + if (v->getField()->editable && (v->containsPoint(mousePoint(ev,*v)) || + (nativeLook() && (y = ev.GetY() + GetScrollPos(wxVERTICAL)) >= v->getStyle()->top + && y < v->getStyle()->bottom))) { + return v.get(); + } + } + return nullptr; +} + void DataEditor::onLoseCapture(wxMouseCaptureLostEvent&) { // We already test for wrong release with HasCapture() // but stupid wxwidget people decided to throw assertion failures diff --git a/src/gui/control/card_editor.hpp b/src/gui/control/card_editor.hpp index 30498c74..17d8f95b 100644 --- a/src/gui/control/card_editor.hpp +++ b/src/gui/control/card_editor.hpp @@ -93,7 +93,7 @@ class DataEditor : public CardViewer { ValueViewer* current_viewer; ///< The currently selected viewer ValueEditor* current_editor; ///< The currently selected editor, corresponding to the viewer ValueViewer* hovered_viewer; ///< The editor under the mouse cursor - vector by_tab_index; ///< The editable viewers, sorted by tab index + vector viewers_in_search_order; ///< The editable viewers, sorted by tab index, for find/replace private: // --------------------------------------------------- : Events @@ -118,17 +118,22 @@ class DataEditor : public CardViewer { void onLoseFocus(wxFocusEvent&); // --------------------------------------------------- : Functions - - /// Changes the selection to the field at the specified coordinates + + /// Changes the selection to the given field, returns true if selection changed + bool selectViewer(ValueViewer*); /** Sends an event to the event function of the current viewer */ - void selectField(wxMouseEvent& ev, bool (ValueEditor::*event)(const RealPoint&, wxMouseEvent&)); - // selectField, but don't send events - void selectFieldNoEvents(const wxMouseEvent&); + /// Changes the selection to the field at the specified coordinates + void selectViewer(wxMouseEvent& ev, bool (ValueEditor::* event)(const RealPoint&, wxMouseEvent&)); /// Convert mouse coordinates to internal coordinates - RealPoint mousePoint(const wxMouseEvent&, const ValueViewer& viewer); + RealPoint mousePoint(const wxMouseEvent&, const ValueViewer& viewer) const; + /// Field under the mouse cursor, or nullptr if there is none + ValueViewer* mousedOverViewer(const wxMouseEvent&) const; /// Select a field found by tab order, can be viewers.end() bool selectWithTab(vector::iterator const&); + + template + bool search(Iterator begin, Iterator end, FindInfo& find, bool from_start); }; /// By default a DataEditor edits cards