From 7b4d88e4ff90c54b8508fd3e6c3ace2b33f3350f Mon Sep 17 00:00:00 2001 From: Dmitry Khrykin Date: Fri, 24 Apr 2020 18:45:16 +0300 Subject: [PATCH] More encapsulated activities search API --- models/activitylist.cpp | 102 ++++++++++++++++++++++++++--------- models/activitylist.h | 10 +++- models/currenttimemarker.cpp | 19 +++---- models/currenttimemarker.h | 16 +++--- models/strategy.cpp | 2 + models/strategyhistory.cpp | 6 +-- models/strategyhistory.h | 15 ++---- models/utility.h | 6 +++ ui/activitylistwidget.cpp | 45 +++++----------- ui/activitylistwidget.h | 2 - ui/slotsmousehandler.cpp | 1 + 11 files changed, 132 insertions(+), 92 deletions(-) diff --git a/models/activitylist.cpp b/models/activitylist.cpp index 16588f2..4887a1a 100644 --- a/models/activitylist.cpp +++ b/models/activitylist.cpp @@ -2,15 +2,10 @@ // Created by Dmitry Khrykin on 2019-07-03. // -#include "activitylist.h" -#include #include #include -#include "utility.h" -#include -#include -#include +#include "activitylist.h" #include "utility.h" void stg::activity_list::silently_add(const activity &activity) { @@ -18,9 +13,10 @@ void stg::activity_list::silently_add(const activity &activity) { throw already_present_exception(); } - std::cout << "add_activity: " << activity << "\n"; - _data.push_back(std::make_shared(activity)); + + if (!search_query.empty()) + search(search_query); } void stg::activity_list::add(const activity &activity) { @@ -31,6 +27,9 @@ void stg::activity_list::add(const activity &activity) { void stg::activity_list::silently_remove_at_index(stg::activity_index index) { _data.erase(_data.begin() + index); + + if (!search_query.empty()) + search(search_query); } void stg::activity_list::remove_at_index(activity_index index) { @@ -48,6 +47,10 @@ void stg::activity_list::silently_edit_at_index(activity_index index, const acti } _data[index] = std::make_shared(new_activity); + + if (!search_query.empty()) { + search(search_query); + } } void stg::activity_list::edit_at_index(activity_index index, const activity &new_activity) { @@ -98,17 +101,25 @@ std::string stg::activity_list::class_print_name() const { stg::activity_list::activity_list(const std::vector &from_vector) { std::transform(from_vector.begin(), from_vector.end(), - std::back_inserter(_data), [](auto &activity) { - return std::make_shared(activity); - }); + std::back_inserter(_data), + [](auto &activity) { + return std::make_shared(activity); + }); + + if (!search_query.empty()) + search(search_query); } stg::activity_list::activity_list(const std::vector> &from_vector) { std::transform(from_vector.begin(), from_vector.end(), - std::back_inserter(_data), [](auto &activity) { - return activity; - }); + std::back_inserter(_data), + [](auto &activity) { + return activity; + }); + + if (!search_query.empty()) + search(search_query); } const stg::activity &stg::activity_list::operator[](activity_index item_index) const { @@ -145,29 +156,68 @@ stg::activity_list::index_of(const activity &activity) const { return std::distance(_data.begin(), it); } -stg::activity_list -stg::activity_list::search(std::string query) const { - query = std::regex_replace(query, std::regex("^\\s*"), ""); - query = std::regex_replace(query, std::regex("\\s*$"), ""); +bool stg::activity_list::search(std::string query) const { + text::strip_bounding_whitespaces(query); + + search_query = query; if (query.empty()) { - return activity_list{}; + auto was_updated = !search_results.empty(); + search_results.clear(); + + return was_updated; } query = text::utf8_fold_case(query); - std::vector> results; + activity_list::data_t results; std::copy_if(begin(), end(), - std::back_inserter(results), [&query](auto activity) { - auto name = activity->name(); + std::back_inserter(results), + [&query](auto activity) { + auto name = text::utf8_fold_case(activity->name()); + return name.find(query) != std::string::npos; + }); + + auto was_updated = search_results != results; + + search_results = results; + + return was_updated; +} + +const stg::activity_list::data_t &stg::activity_list::filtered() const { + if (search_query.empty()) + return _data; + + return search_results; +} + +std::optional +stg::activity_list::index_from_filtered(index_t index_in_filtered) const { + auto *activity = filtered().at(index_in_filtered).get(); + return index_of(activity); +} + +std::optional +stg::activity_list::index_in_filtered(index_t activity_index) const { + auto activity = _data[activity_index]; + auto it = std::find(filtered().begin(), + filtered().end(), + activity); + + if (it == filtered().end()) + return std::nullopt; + + return std::distance(filtered().begin(), it); +} - name = text::utf8_fold_case(name); - return name.find(query) != std::string::npos; - }); +void stg::activity_list::reset_with(data_t data) { + activity_list_base::reset_with(data); - return activity_list(results); + if (!search_query.empty()) + search(search_query); } const char *stg::activity_list::already_present_exception::what() const noexcept { diff --git a/models/activitylist.h b/models/activitylist.h index 42a4426..e057bb6 100644 --- a/models/activitylist.h +++ b/models/activitylist.h @@ -38,12 +38,20 @@ namespace stg { std::optional index_of(const activity *activity) const; std::optional index_of(const activity &activity) const; - activity_list search(std::string query) const; + bool search(std::string query) const; + const activity_list::data_t &filtered() const; + std::optional index_from_filtered(index_t index_in_filtered) const; + std::optional index_in_filtered(index_t activity_index) const; + + void reset_with(data_t data) override; std::string class_print_name() const override; private: friend strategy; + mutable std::string search_query; + mutable activity_list::data_t search_results; + void silently_add(const activity &activity) noexcept(false); void add(const activity &activity) noexcept(false); diff --git a/models/currenttimemarker.cpp b/models/currenttimemarker.cpp index d01c28d..7152a23 100644 --- a/models/currenttimemarker.cpp +++ b/models/currenttimemarker.cpp @@ -8,7 +8,7 @@ stg::current_time_marker::current_time_marker(const stg::strategy &strategy) : strategy(strategy) {} -int stg::current_time_marker::top_offset_in(int total_height) const { +auto stg::current_time_marker::top_offset_in(int total_height) const -> int { auto relative = relative_position(); if (relative < 0) return 0; @@ -17,20 +17,20 @@ int stg::current_time_marker::top_offset_in(int total_height) const { return 1; } - return static_cast(total_height * relative_position()); + return static_cast(static_cast(total_height) * relative_position()); } -bool stg::current_time_marker::is_visible() const { +auto stg::current_time_marker::is_visible() const -> bool { auto relative = relative_position(); return relative >= 0 && relative <= 1; } -bool stg::current_time_marker::is_hidden() const { +auto stg::current_time_marker::is_hidden() const -> bool { return !is_visible(); } -stg::rect stg::current_time_marker::rect_in_parent(const rect &parent_rect, - int marker_radius) const { +auto stg::current_time_marker::rect_in_parent(const rect &parent_rect, + int marker_radius) const -> stg::rect { return rect{ parent_rect.left - marker_radius, parent_rect.top + top_offset_in(parent_rect.height) - marker_radius, @@ -39,8 +39,8 @@ stg::rect stg::current_time_marker::rect_in_parent(const rect &parent_rect, }; } -int stg::current_time_marker::scroll_offset_in_parent(const rect &parent_rect, - int window_height) const { +auto stg::current_time_marker::scroll_offset_in_parent(const rect &parent_rect, + int window_height) const -> int { auto rect = rect_in_parent(parent_rect); auto top_offset = rect.top - window_height / 2; @@ -53,9 +53,10 @@ int stg::current_time_marker::scroll_offset_in_parent(const rect &parent_rect, return top_offset; } -float stg::current_time_marker::relative_position() const { +auto stg::current_time_marker::relative_position() const -> float { auto strategy_duration_seconds = strategy.duration() * 60; auto start_of_strategy_seconds = strategy.begin_time() * 60; + return static_cast(stg::time_utils::current_seconds() - start_of_strategy_seconds) / strategy_duration_seconds; } diff --git a/models/currenttimemarker.h b/models/currenttimemarker.h index 8f9fc01..c91a1cf 100644 --- a/models/currenttimemarker.h +++ b/models/currenttimemarker.h @@ -14,19 +14,19 @@ namespace stg { public: explicit current_time_marker(const strategy &strategy); - bool is_visible() const; - bool is_hidden() const; + auto is_visible() const -> bool; + auto is_hidden() const -> bool; - rect rect_in_parent(const rect &parent_rect, - int marker_radius = 0) const; + auto rect_in_parent(const rect &parent_rect, + int marker_radius = 0) const -> rect; - int scroll_offset_in_parent(const rect &parent_rect, - int window_height) const; + auto scroll_offset_in_parent(const rect &parent_rect, + int window_height) const -> int; private: const strategy &strategy; - int top_offset_in(int total_height) const; - float relative_position() const; + auto top_offset_in(int total_height) const -> int; + auto relative_position() const -> float; }; } diff --git a/models/strategy.cpp b/models/strategy.cpp index c08f7c5..3fb6f79 100644 --- a/models/strategy.cpp +++ b/models/strategy.cpp @@ -58,11 +58,13 @@ const stg::activity_list &stg::strategy::activities() const { void stg::strategy::silently_add_activity(const activity &activity) { _activities.silently_add(activity); + commit_to_history(); } void stg::strategy::add_activity(const activity &activity) { _activities.add(activity); + commit_to_history(); } diff --git a/models/strategyhistory.cpp b/models/strategyhistory.cpp index d70c43d..eef749a 100644 --- a/models/strategyhistory.cpp +++ b/models/strategyhistory.cpp @@ -4,6 +4,9 @@ #include "strategyhistory.h" +stg::strategy_history::strategy_history(entry current_state) + : current_state(std::move(current_state)) {} + bool stg::strategy_history::commit(const entry &new_state) { if (new_state != current_state) { undo_stack.push_back(current_state); @@ -51,9 +54,6 @@ bool stg::strategy_history::has_next_state() { return !redo_stack.empty(); } -stg::strategy_history::strategy_history(stg::strategy_history::entry current_state) - : current_state(std::move(current_state)) {} - bool stg::strategy_history::has_prevoius_activities_state() { if (!has_prevoius_state()) return false; diff --git a/models/strategyhistory.h b/models/strategyhistory.h index d3e5046..6bc7813 100644 --- a/models/strategyhistory.h +++ b/models/strategyhistory.h @@ -19,17 +19,10 @@ namespace stg { time_slots_state::data_t time_slots; friend bool operator==(const entry &lhs, const entry &rhs) { - auto &bigger_container = lhs.activities.size() >= rhs.activities.size() - ? lhs.activities - : rhs.activities; - - auto &smaller_container = lhs.activities.size() >= rhs.activities.size() - ? rhs.activities - : lhs.activities; - - auto activities_are_equal = std::equal(smaller_container.begin(), - smaller_container.end(), - bigger_container.begin(), + auto activities_are_equal = std::equal(lhs.activities.begin(), + lhs.activities.end(), + rhs.activities.begin(), + rhs.activities.end(), [](const auto &l, const auto &r) { return *l == *r; }); diff --git a/models/utility.h b/models/utility.h index 8b0610d..3eec919 100644 --- a/models/utility.h +++ b/models/utility.h @@ -6,6 +6,7 @@ #define STRATEGR_UTILITY_H #include +#include #include #include @@ -24,5 +25,10 @@ namespace stg::text { auto *lowered = (char *) utf8proc_NFKC_Casefold((utf8proc_uint8_t *) str.c_str()); return std::string(lowered); } + + inline void strip_bounding_whitespaces(std::string &str) { + str = std::regex_replace(str, std::regex("^\\s*"), ""); + str = std::regex_replace(str, std::regex("\\s*$"), ""); + } } #endif //STRATEGR_UTILITY_H diff --git a/ui/activitylistwidget.cpp b/ui/activitylistwidget.cpp index 5be2671..92e840f 100644 --- a/ui/activitylistwidget.cpp +++ b/ui/activitylistwidget.cpp @@ -115,9 +115,6 @@ void ActivityListWidget::layoutChildWidgets() { &ActivityEditorMenu::submitActivity, [&](const stg::activity &activity) { strategy.add_activity(activity); - if (isShowingSearchResults()) { - performSearch(); - } }); } @@ -130,7 +127,7 @@ void ActivityListWidget::setSelectedForItemAtIndex(int index, bool isSelected) c } void ActivityListWidget::deselectAllItems() { - for (size_t i = 0; i < strategy.activities().size(); i++) { + for (size_t i = 0; i < numberOfItems(); i++) { setSelectedForItemAtIndex(i, false); } @@ -171,10 +168,7 @@ void ActivityListWidget::reconnectItemAtIndex(int itemIndex, ActivityWidget *item) { item->disconnect(); - auto activityIndex = itemIndex; - if (isShowingSearchResults()) { - activityIndex = *strategy.activities().index_of(searchResults.at(itemIndex)); - } + auto activityIndex = *strategy.activities().index_from_filtered(itemIndex); connect(item, &ActivityWidget::selected, [=] { strategy.place_activity(activityIndex, mainScene()->selection()); @@ -185,18 +179,10 @@ void ActivityListWidget::reconnectItemAtIndex(int itemIndex, connect(item, &ActivityWidget::activityDeleted, [=] { strategy.delete_activity(activityIndex); - - if (isShowingSearchResults()) { - performSearch(); - } }); connect(item, &ActivityWidget::activityEdited, [=](const stg::activity &newActivity) { strategy.edit_activity(activityIndex, newActivity); - - if (isShowingSearchResults()) { - performSearch(); - } }); connect(item, &ActivityWidget::hovered, [=] { @@ -209,14 +195,14 @@ void ActivityListWidget::reconnectItemAtIndex(int itemIndex, }); } -bool ActivityListWidget::isShowingSearchResults() const { - return !QRegExp("\\s*").exactMatch(searchBox->text()); -} - void ActivityListWidget::performSearch() { - searchResults = strategy.activities().search(searchBox->text().toStdString()); - updateList(); - deselectAllItems(); + auto searchQuery = searchBox->text().toStdString(); + auto needsToUpdateList = strategy.activities().search(searchQuery); + + if (needsToUpdateList) { + updateList(); + deselectAllItems(); + } } void ActivityListWidget::showNewActivityMenu() { @@ -240,9 +226,7 @@ void ActivityListWidget::reloadStrategy() { } int ActivityListWidget::numberOfItems() { - return isShowingSearchResults() - ? searchResults.size() - : strategy.activities().size(); + return strategy.activities().filtered().size(); } QVBoxLayout *ActivityListWidget::listLayout() { @@ -250,17 +234,14 @@ QVBoxLayout *ActivityListWidget::listLayout() { } void ActivityListWidget::reuseItemAtIndex(int index, ActivityWidget *itemWidget) { - auto activity = isShowingSearchResults() - ? searchResults.at(index) - : strategy.activities().at(index); + auto activity = strategy.activities().filtered().at(index).get(); + itemWidget->setActivity(activity); reconnectItemAtIndex(index, itemWidget); } ActivityWidget *ActivityListWidget::makeNewItemAtIndex(int index) { - auto activity = isShowingSearchResults() - ? searchResults.at(index) - : strategy.activities().at(index); + auto activity = strategy.activities().filtered().at(index).get(); auto itemWidget = new ActivityWidget(activity); reconnectItemAtIndex(index, itemWidget); diff --git a/ui/activitylistwidget.h b/ui/activitylistwidget.h index 3bcaf24..08a6a72 100644 --- a/ui/activitylistwidget.h +++ b/ui/activitylistwidget.h @@ -35,7 +35,6 @@ Q_OBJECT bool eventFilter(QObject *object, QEvent *event) override; private: stg::strategy &strategy; - stg::activity_list searchResults; SearchBoxWidget *searchBox = nullptr; QScrollArea *scrollArea = nullptr; @@ -57,7 +56,6 @@ Q_OBJECT void getBack(); void performSearch(); - bool isShowingSearchResults() const; void setSelectedForItemAtIndex(int index, bool isSelected) const; void deselectAllItems(); diff --git a/ui/slotsmousehandler.cpp b/ui/slotsmousehandler.cpp index dd4cf67..7d2b9ec 100644 --- a/ui/slotsmousehandler.cpp +++ b/ui/slotsmousehandler.cpp @@ -81,6 +81,7 @@ SlotsMouseHandler::SlotsMouseHandler(SlotsWidget *slotsWidget) draggedSessionAnimation = new QPropertyAnimation(draggedSessionWidget, "geometry"); draggedSessionAnimation->setDuration(50); + auto smallRect = getRect(); auto bigRect = makeBigRect(smallRect);