diff --git a/CMakeLists.txt b/CMakeLists.txt index 95bb90e..fadd82e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,15 @@ set(CMAKE_CXX_STANDARD 17) find_package(Boost COMPONENTS filesystem REQUIRED) find_package(catch2 REQUIRED) find_package(Qt5 COMPONENTS Widgets Network Test REQUIRED) -find_library(uf8Proc_LIBRARY_PATH utf8Proc) +find_package(nlohmann_json REQUIRED) + +find_library(uf8Proc_LIBRARY_PATH libutf8proc.a libutf8proc.lib utf8proc) if (APPLE) find_package(Qt5 COMPONENTS MacExtras REQUIRED) endif () -find_package(nlohmann_json REQUIRED) +message(${uf8Proc_LIBRARY_PATH}) include_directories(.) include_directories(models) @@ -78,10 +80,10 @@ set(MODELS ${MODELS_PLATFORM_FILES} models/notifier.cpp models/notifier.h models/utility.h - models/mousehandler.cpp - models/mousehandler.h + models/mouse_handler.cpp + models/mouse_handler.h models/mousehandleroperations.cpp - models/mousehandleroperations.h) + models/mousehandleroperations.h models/selection.cpp models/selection.h models/event.cpp models/event.h) set(MODELS_TESTS models/tests/activity_tests.cpp @@ -114,16 +116,16 @@ set(UI ui/sessionsmainwidget.h ui/activitylistwidget.cpp ui/activitylistwidget.h - ui/activitylistitemwidget.cpp - ui/activitylistitemwidget.h + ui/activitywidget.cpp + ui/activitywidget.h ui/strategysettingswidget.cpp ui/strategysettingswidget.h - ui/slotboard.cpp - ui/slotboard.h + ui/slotboardwidget.cpp + ui/slotboardwidget.h ui/slidinganimator.cpp ui/slidinganimator.h ui/slotswidget.cpp - ui/slotboard.h + ui/slotboardwidget.h ui/slotruler.cpp ui/slotruler.h ui/sessionwidget.cpp @@ -176,7 +178,7 @@ set(UI ui/searchbox.cpp ui/searchbox.h ui/dynamicpalette.cpp - ui/dynamicpalette.h) + ui/dynamicpalette.h ui/slotboardcircleswidget.cpp ui/slotboardcircleswidget.h) set(UTILITY ${version_file} diff --git a/models/event.cpp b/models/event.cpp new file mode 100644 index 0000000..6b08b28 --- /dev/null +++ b/models/event.cpp @@ -0,0 +1,42 @@ +// +// Created by Dmitry Khrykin on 2020-01-30. +// + +#include "event.h" + +stg::event::event(stg::event::key_modifiers modifiers) + : modifiers(modifiers) { +} + +stg::mouse_event::mouse_event(const stg::point &position, + stg::event::key_modifiers modifiers) + : event(modifiers), + position(position) { +} + +bool stg::event::is(stg::event::key_modifiers mod) const { + return modifiers == mod; +} + +bool stg::event::with(stg::event::key_modifiers mod) const { + return (modifiers & mod) == mod; +} + +std::ostream &stg::operator<<(std::ostream &os, const stg::mouse_event &e) { + os << "mouse_event {\n"; + os << " position: [" << e.position.x << ", " << e.position.y << "]\n"; + os << " modifiers: ["; + if (e.with(mouse_event::left_key)) { + os << " left_key "; + } + if (e.with(mouse_event::right_key)) { + os << " right_key "; + } + if (e.with(mouse_event::ctrl_key)) { + os << " ctrl_key "; + } + os << "]\n"; + os << "}\n"; + + return os; +} \ No newline at end of file diff --git a/models/event.h b/models/event.h new file mode 100644 index 0000000..37c597c --- /dev/null +++ b/models/event.h @@ -0,0 +1,51 @@ +// +// Created by Dmitry Khrykin on 2020-01-30. +// + +#ifndef STRATEGR_EVENT_H +#define STRATEGR_EVENT_H + +#include "geometry.h" + +namespace stg { + struct event { + using key_modifiers = uint8_t; + + static constexpr key_modifiers left_key = 1u << 0u; + static constexpr key_modifiers right_key = 1u << 1u; + static constexpr key_modifiers ctrl_key = 1u << 2u; + + explicit event(key_modifiers modifiers); + + template + event(QtLikeEvent *evt) { + if (evt->modifiers() == 0x04000000) { + modifiers |= ctrl_key; + }; + } + + bool is(key_modifiers mod) const; + bool with(key_modifiers mod) const; + + key_modifiers modifiers = 0; + }; + + struct mouse_event : public event { + mouse_event(const point &position, key_modifiers modifiers); + + template + mouse_event(QtLikeEvent *evt) : event(evt), position(evt->pos()) { + if (evt->buttons() == 1) { + modifiers |= left_key; + } else if (evt->buttons() == 2) { + modifiers |= right_key; + } + } + + friend std::ostream &operator<<(std::ostream &os, const mouse_event &e); + point position; + }; +}; + + +#endif //STRATEGR_EVENT_H diff --git a/models/geometry.h b/models/geometry.h index 8741205..fac1f52 100644 --- a/models/geometry.h +++ b/models/geometry.h @@ -55,6 +55,8 @@ namespace stg { int x = 0; int y = 0; + explicit point(int x = 0, int y = 0) : x(x), y(y) {} + /** * Implicit constructor for Qt-like point */ diff --git a/models/mouse_handler.cpp b/models/mouse_handler.cpp new file mode 100644 index 0000000..7c4d082 --- /dev/null +++ b/models/mouse_handler.cpp @@ -0,0 +1,289 @@ +// +// Created by Dmitry Khrykin on 2020-01-29. +// + +#include "mouse_handler.h" +#include "mousehandleroperations.h" + +#define stg_make_operation(operation) (std::make_unique(this)) + +stg::mouse_handler::mouse_handler(stg::strategy &strategy, + stg::selection &selection, + std::function slot_height_getter, + std::function bounds_getter, + const mouse_parameters &settings) + : strategy(strategy), + selection(selection), + get_slot_height(std::move(slot_height_getter)), + get_bounds(std::move(bounds_getter)), + settings(settings), + current_operaion(stg_make_operation(none_operation)) { + assert(slot_height_getter != nullptr && "slot_height_getter must be provided"); + assert(bounds_getter != nullptr && "bounds_getter must be provided"); +} + +stg::mouse_handler::~mouse_handler() = default; + +void stg::mouse_handler::mouse_press(const stg::mouse_event &event) { +// std::cout << "mouse_press: " << event << "\n"; +// std::cout << "bounds: " << get_bounds() << "\n"; +// std::cout << "slot_index: " << get_slot_index(event) << "\n"; + + current_slot_index = get_slot_index(event); + current_session_index = get_session_index(current_slot_index); + current_mouse_zone = get_mouse_zone(current_session_index, event.position); + + current_operaion = get_operation(event.modifiers); + current_operaion->init(event); + + std::cout << "current_operaion: " << current_operaion->type() << "\n"; + + update_cursor(event.modifiers); +} + +void stg::mouse_handler::mouse_move(const stg::mouse_event &event) { + current_slot_index = get_slot_index(event); + current_session_index = get_session_index(current_slot_index); + current_mouse_zone = get_mouse_zone(current_session_index, event.position); + + current_operaion->perform(event); + + update_cursor(event.modifiers); +} + +void stg::mouse_handler::mouse_release(const stg::mouse_event &event) { + std::cout << "mouse_release" << "\n"; + + current_operaion->teardown(event); + current_operaion = stg_make_operation(none_operation); + + update_cursor(event.modifiers); + + // Reset these after we update cursor + current_mouse_zone = mouse_zone::out_of_bounds; + current_slot_index = -1; + current_session_index = -1; +} + +void stg::mouse_handler::key_down(const event &event) { +// std::cout << "key_down" << "\n"; + update_cursor(event.modifiers); +} + +void stg::mouse_handler::key_up(const event &event) { +// std::cout << "key_up" << "\n"; + update_cursor(event.modifiers); +} + +std::unique_ptr +stg::mouse_handler::get_operation(event::key_modifiers modifiers) { + auto zone = current_mouse_zone; + auto &time_slots = strategy.time_slots(); + auto ¤t_slot = time_slots[current_slot_index]; + + auto next_empty = time_slots.next_slot_empty(current_slot_index); + auto prev_empty = time_slots.previous_slot_empty(current_slot_index); + + auto current_selected = selection.has_selected(current_slot_index); + + if (modifiers == mouse_event::left_key) { + if (!selection.empty() && current_selected) { + return stg_make_operation(select_operation); + } + + if (zone == mouse_zone::drag) { + if (current_slot.empty()) + return stg_make_operation(select_operation); + else + return stg_make_operation(drag_operation); + } else if (zone == mouse_zone::stretch_bottom) { + if (current_slot.empty() && !next_empty) + return stg_make_operation(resize_operation); + else if (current_slot.empty()) + return stg_make_operation(select_operation); + else + return stg_make_operation(resize_operation); + } else if (zone == mouse_zone::stretch_top) { + if (current_slot.empty() && !prev_empty) + return stg_make_operation(resize_operation); + else if (current_slot.empty()) + return stg_make_operation(select_operation); + else + return stg_make_operation(resize_operation); + } + } else if (modifiers == mouse_event::right_key) { + + } else if (modifiers == (mouse_event::left_key | mouse_event::ctrl_key)) { + return stg_make_operation(select_operation); + } + + return stg_make_operation(none_operation); +} + +stg::mouse_handler::index_t +stg::mouse_handler::get_session_index(index_t slot_index) { + return strategy.sessions().session_index_for_time_slot_index(slot_index); +} + +stg::mouse_handler::range stg::mouse_handler::get_session_range(index_t session_index) { + const auto &session = strategy.sessions()[session_index]; + auto top = static_cast(strategy.sessions().relative_time(session) * px_in_time()); + auto height = static_cast(session.duration() * px_in_time()); + +// std::cout << "slot_index: " << slot_index << "\n"; +// std::cout << "session_index: " << session_index << "\n"; + + return range{.top = top, .bottom = top + height}; +} + +stg::mouse_handler::mouse_zone stg::mouse_handler::get_mouse_zone(int session_index, + const point &mouse_pos) { + auto session_range = get_session_range(session_index); + + auto top_stretch_zone = range{ + .top = session_range.top, + .bottom = session_range.top + settings.stretch_zone_height + }; + + auto bottom_stretch_zone = range{ + .top = session_range.bottom - settings.stretch_zone_height, + .bottom = session_range.bottom + }; + +// std::cout << "pos: " << event.position << "\n"; +// std::cout << "session_range: " << session_range << "\n"; +// std::cout << "top_stretch_zone: " << top_stretch_zone << "\n"; +// std::cout << "bottom_stretch_zone: " << bottom_stretch_zone << "\n"; + + if (!session_range.contains(mouse_pos.y)) { + return mouse_zone::out_of_bounds; + } + + if (top_stretch_zone.contains(mouse_pos.y)) { + return mouse_zone::stretch_top; + } else if (bottom_stretch_zone.contains(mouse_pos.y)) { + return mouse_zone::stretch_bottom; + } else { + return mouse_zone::drag; + } +} + +float stg::mouse_handler::px_in_time() { + return get_slot_height() / static_cast(strategy.time_slot_duration()); +} + +stg::mouse_handler::index_t stg::mouse_handler::get_slot_index(const stg::mouse_event &event) { + auto index = event.position.y / get_slot_height(); + + if (index < 0) + index = 0; + else if (index > strategy.number_of_time_slots() - 1) + index = strategy.number_of_time_slots() - 1; + + return index; +} + +void stg::mouse_handler::update_cursor(event::key_modifiers modifiers) { + auto new_cursor = get_cursor(modifiers); + if (new_cursor == current_cursor) + return; + + current_cursor = new_cursor; + + if (on_cursor_change) + on_cursor_change(new_cursor); +} + +stg::mouse_handler::cursor +stg::mouse_handler::get_cursor(event::key_modifiers modifiers) { + auto mouse_zone = current_mouse_zone; + auto &time_slots = strategy.time_slots(); + auto ¤t_slot = time_slots[current_slot_index]; + auto next_empty = time_slots.next_slot_empty(current_slot_index); + auto prev_empty = time_slots.previous_slot_empty(current_slot_index); + + if (selection.has_selected(current_slot_index)) { + return cursor::pointer; + } + + switch (current_operaion->type()) { + case none: + if ((modifiers & mouse_event::ctrl_key) == mouse_event::ctrl_key) { + return cursor::pointer; + } + + switch (mouse_zone) { + case mouse_zone::out_of_bounds: + return cursor::pointer; + case mouse_zone::drag: + return current_slot.empty() + ? cursor::pointer + : cursor::open_hand; + case mouse_zone::stretch_top: + return !current_slot.empty() || + (current_slot.empty() && !prev_empty) + ? cursor::resize + : cursor::pointer; + case mouse_zone::stretch_bottom: + return !current_slot.empty() || + (current_slot.empty() && !next_empty) + ? cursor::resize + : cursor::pointer; + + } + case drag: + return cursor::closed_hand; + case resize: + return cursor::resize; + case select: + return cursor::pointer; + } +} + +std::ostream &stg::operator<<(std::ostream &os, stg::mouse_handler::mouse_zone zone) { + switch (zone) { + case mouse_handler::mouse_zone::stretch_top: + os << "stretch_top"; + break; + case mouse_handler::mouse_zone::stretch_bottom: + os << "stretch_bottom"; + break; + case mouse_handler::mouse_zone::drag : + os << "drag"; + break; + case mouse_handler::mouse_zone::out_of_bounds : + os << "out_of_bounds"; + break; + } + + return os; +} + +std::ostream &stg::operator<<(std::ostream &os, const stg::mouse_handler::range &range) { + os << "range [ " << range.top << " " << range.bottom << " ]"; + return os; +} + +bool stg::mouse_handler::range::contains(int coord) { + return coord >= top && coord <= bottom; +} + +std::ostream &stg::operator<<(std::ostream &os, mouse_handler::operation_type op) { + switch (op) { + case mouse_handler::none: + os << "none"; + break; + case mouse_handler::drag: + os << "drag"; + break; + case mouse_handler::resize: + os << "resize"; + break; + case mouse_handler::select: + os << "select"; + break; + } + + return os; +} + diff --git a/models/mouse_handler.h b/models/mouse_handler.h new file mode 100644 index 0000000..9736d27 --- /dev/null +++ b/models/mouse_handler.h @@ -0,0 +1,126 @@ +// +// Created by Dmitry Khrykin on 2020-01-29. +// + +#ifndef STRATEGR_MOUSE_HANDLER_H +#define STRATEGR_MOUSE_HANDLER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "geometry.h" +#include "strategy.h" +#include "event.h" +#include "selection.h" + +namespace stg { + struct mouse_parameters { + int stretch_zone_height = 5; + unsigned int resolution = 2; + }; + + class mouse_handler { + public: + enum class cursor { + pointer, + closed_hand, + open_hand, + resize + }; + + struct range { + int top = 0; + int bottom = 0; + + bool contains(int coord); + + friend std::ostream &operator<<(std::ostream &os, const range &range); + }; + + std::function on_cursor_change = nullptr; + std::function on_open_activities = nullptr; + std::function on_show_context_menu = nullptr; + std::function &)> on_select_sessions = nullptr; + std::function on_resize_boundary_change = nullptr; + + explicit mouse_handler(stg::strategy &strategy, + stg::selection &selection, + std::function slot_height_getter, + std::function bounds_getter, + const mouse_parameters &settings = mouse_parameters()); + + ~mouse_handler(); + + void mouse_press(const mouse_event &event); + void mouse_move(const mouse_event &event); + void mouse_release(const mouse_event &event); + void key_down(const event &event); + void key_up(const event &event); + private: + enum class mouse_zone { + out_of_bounds = 0, + drag, + stretch_top, + stretch_bottom + }; + + enum operation_type { + none = 0, + drag, + resize, + select + }; + + struct operation; + struct none_operation; + struct drag_operation; + friend struct drag_operation; + + struct resize_operation; + friend struct resize_operation; + + struct select_operation; + friend struct select_operation; + + using index_t = stg::strategy::time_slot_index_t; + + std::function get_bounds = nullptr; + std::function get_slot_height = nullptr; + + stg::strategy &strategy; + stg::selection &selection; + + mouse_parameters settings; + + cursor current_cursor = cursor::pointer; + mouse_zone current_mouse_zone = mouse_zone::out_of_bounds; + index_t current_slot_index = -1; + index_t current_session_index = -1; + + std::unique_ptr current_operaion; + + float px_in_time(); + + index_t get_slot_index(const mouse_event &event); + index_t get_session_index(index_t slot_index); + + range get_session_range(index_t session_index); + + stg::mouse_handler::mouse_zone get_mouse_zone(int session_index, const point &mouse_pos); + std::unique_ptr get_operation(event::key_modifiers modifiers); + + cursor get_cursor(event::key_modifiers modifiers); + void update_cursor(event::key_modifiers modifiers); + + friend std::ostream &operator<<(std::ostream &os, mouse_zone zone); + friend std::ostream &operator<<(std::ostream &os, operation_type op); + }; +} + + +#endif //STRATEGR_MOUSE_HANDLER_H diff --git a/models/mousehandler.cpp b/models/mousehandler.cpp deleted file mode 100644 index 339727b..0000000 --- a/models/mousehandler.cpp +++ /dev/null @@ -1,258 +0,0 @@ -// -// Created by Dmitry Khrykin on 2020-01-29. -// - -#include "mousehandler.h" -#include "mousehandleroperations.h" - -#define make_operation(operation) (std::make_unique(this)) - -stg::mousehandler::mousehandler(stg::strategy &strategy, - std::function slot_height_getter, - std::function bounds_getter, - const mousehandler_parameters &settings) - : strategy(strategy), - get_slot_height(std::move(slot_height_getter)), - get_bounds(std::move(bounds_getter)), - settings(settings), - current_operaion(make_operation(none_operation)) { - assert(slot_height_getter != nullptr && "slot_height_getter must be provided"); - assert(bounds_getter != nullptr && "bounds_getter must be provided"); -} - -stg::mousehandler::~mousehandler() = default; - -void stg::mousehandler::mouse_press(const stg::mouse_event &event) { -// std::cout << "mouse_press: " << event << "\n"; -// std::cout << "bounds: " << get_bounds() << "\n"; -// std::cout << "slot_index: " << get_slot_index(event) << "\n"; - -// current_mouse_zone = get_mouse_zone(event); - current_operaion = get_operation(event); - current_operaion->init(event); - - std::cout << "current_operaion: " << current_operaion->type() << "\n"; - - update_cursor(event); -} - -void stg::mousehandler::mouse_move(const stg::mouse_event &event) { - current_operaion->perform(event); - - update_cursor(event); -} - -void stg::mousehandler::mouse_release(const stg::mouse_event &event) { - current_operaion->teardown(event); - current_operaion = make_operation(none_operation); - - update_cursor(event); -} - -std::unique_ptr -stg::mousehandler::get_operation(const mouse_event &event) { - auto zone = get_mouse_zone(event); - auto current_slot_index = get_slot_index(event); - auto &time_slots = strategy.time_slots(); - auto ¤t_slot = time_slots[current_slot_index]; - auto next_empty = time_slots.next_slot_empty(current_slot_index); - auto prev_empty = time_slots.previous_slot_empty(current_slot_index); - - if (event.is(mouse_event::left_key)) { - if (zone == mouse_zone::drag) { - if (current_slot.empty()) - return make_operation(select_operation); - else - return make_operation(drag_operation); - } else if (zone == mouse_zone::stretch_bottom) { - if (current_slot.empty() && !next_empty) - return make_operation(stretch_bottom_operation); - else if (current_slot.empty()) - return make_operation(select_operation); - else - return make_operation(stretch_bottom_operation); - } else if (zone == mouse_zone::stretch_top) { - if (current_slot.empty() && !prev_empty) - return make_operation(stretch_top_operation); - else if (current_slot.empty()) - return make_operation(select_operation); - else - return make_operation(stretch_top_operation); - } - } else if (event.is(mouse_event::right_key)) { - - } else if (event.is(mouse_event::left_key | mouse_event::ctrl_key)) { - return make_operation(select_operation); - } - - return make_operation(none_operation); -} - -stg::mousehandler::index_t -stg::mousehandler::get_session_index(stg::mousehandler::index_t slot_index) { - return strategy.sessions().session_index_for_time_slot_index(slot_index); -} - -stg::mousehandler::range stg::mousehandler::get_session_range(const stg::mouse_event &event) { - auto slot_index = get_slot_index(event); - auto session_index = get_session_index(slot_index); - const auto &session = strategy.sessions()[session_index]; - auto top = static_cast(strategy.sessions().relative_time(session) * px_in_time()); - auto height = static_cast(session.duration() * px_in_time()); - -// std::cout << "slot_index: " << slot_index << "\n"; -// std::cout << "session_index: " << session_index << "\n"; - - return range{.top = top, .bottom = top + height}; -} - -stg::mousehandler::mouse_zone stg::mousehandler::get_mouse_zone(stg::mouse_event event) { - auto session_range = get_session_range(event); - - auto top_stretch_zone = range{ - .top = session_range.top, - .bottom = session_range.top + settings.stretch_zone_height - }; - - auto bottom_stretch_zone = range{ - .top = session_range.bottom - settings.stretch_zone_height, - .bottom = session_range.bottom - }; - -// std::cout << "pos: " << event.position << "\n"; -// std::cout << "session_range: " << session_range << "\n"; -// std::cout << "top_stretch_zone: " << top_stretch_zone << "\n"; -// std::cout << "bottom_stretch_zone: " << bottom_stretch_zone << "\n"; - - if (!session_range.contains(event.position.y)) { - return mouse_zone::out_of_bounds; - } - - if (top_stretch_zone.contains(event.position.y)) { - return mouse_zone::stretch_top; - } else if (bottom_stretch_zone.contains(event.position.y)) { - return mouse_zone::stretch_bottom; - } else { - return mouse_zone::drag; - } -} - -float stg::mousehandler::px_in_time() { - return get_slot_height() / static_cast(strategy.time_slot_duration()); -} - -stg::mousehandler::index_t stg::mousehandler::get_slot_index(const stg::mouse_event &event) { - return event.position.y / get_slot_height(); -} - -void stg::mousehandler::update_cursor(const mouse_event &event) { - auto new_cursor = get_cursor(event); - if (new_cursor == current_cursor) - return; - - current_cursor = new_cursor; - - if (on_cursor_change) - on_cursor_change(new_cursor); -} - -void stg::mousehandler::set_selected(bool selected) { - if (on_set_selected) - on_set_selected(selected); -} - -stg::mousehandler::cursor stg::mousehandler::get_cursor(const mouse_event &event) { - auto mouse_zone = get_mouse_zone(event); - auto current_slot_index = get_slot_index(event); - auto &time_slots = strategy.time_slots(); - auto ¤t_slot = time_slots[current_slot_index]; - auto next_empty = time_slots.next_slot_empty(current_slot_index); - auto prev_empty = time_slots.previous_slot_empty(current_slot_index); - - switch (current_operaion->type()) { - case none: - switch (mouse_zone) { - case mouse_zone::out_of_bounds: - return cursor::pointer; - case mouse_zone::drag: - return current_slot.empty() - ? cursor::pointer - : cursor::open_hand; - case mouse_zone::stretch_top: - return !current_slot.empty() || - (current_slot.empty() && !prev_empty) - ? cursor::resize - : cursor::pointer; - case mouse_zone::stretch_bottom: - return !current_slot.empty() || - (current_slot.empty() && !next_empty) - ? cursor::resize - : cursor::pointer; - - } - case drag: - return cursor::closed_hand; - case stretch_top: - case stretch_bottom: - return cursor::resize; - case select: - return cursor::pointer; - } -} - -std::ostream &stg::operator<<(std::ostream &os, stg::mousehandler::mouse_zone zone) { - switch (zone) { - case mousehandler::mouse_zone::stretch_top: - os << "stretch_top"; - break; - case mousehandler::mouse_zone::stretch_bottom: - os << "stretch_bottom"; - break; - case mousehandler::mouse_zone::drag : - os << "drag"; - break; - case mousehandler::mouse_zone::out_of_bounds : - os << "out_of_bounds"; - break; - } - - return os; -} - -std::ostream &stg::operator<<(std::ostream &os, const stg::mousehandler::range &range) { - os << "range [ " << range.top << " " << range.bottom << " ]"; - return os; -} - -std::ostream &stg::operator<<(std::ostream &os, const stg::mouse_event &e) { - os << "mouse_event {\n"; - os << " position: [" << e.position.x << ", " << e.position.y << "]\n"; - os << " modifiers: ["; - if (e.with(mouse_event::left_key)) { - os << " left_key "; - } - if (e.with(mouse_event::right_key)) { - os << " right_key "; - } - if (e.with(mouse_event::ctrl_key)) { - os << " ctrl_key "; - } - os << "]\n"; - os << "}\n"; - - return os; -} - -stg::mouse_event::mouse_event( - const stg::point &position, stg::mouse_event::key_modifiers -modifiers) - : position(position), modifiers(modifiers) { -} - -bool stg::mouse_event::is(stg::mouse_event::key_modifiers mod) const { - return modifiers == mod; -} - -bool stg::mouse_event::with(stg::mouse_event::key_modifiers mod) const { - return (modifiers & mod) == mod; -} diff --git a/models/mousehandler.h b/models/mousehandler.h deleted file mode 100644 index d351a52..0000000 --- a/models/mousehandler.h +++ /dev/null @@ -1,146 +0,0 @@ -// -// Created by Dmitry Khrykin on 2020-01-29. -// - -#ifndef STRATEGR_MOUSEHANDLER_H -#define STRATEGR_MOUSEHANDLER_H - -#include -#include -#include -#include -#include -#include -#include - -#include "geometry.h" -#include "strategy.h" - -namespace stg { - struct mouse_event { - using key_modifiers = uint8_t; - - static constexpr key_modifiers left_key = 1u << 0u; - static constexpr key_modifiers right_key = 1u << 1u; - static constexpr key_modifiers ctrl_key = 1u << 2u; - - mouse_event(const point &position, key_modifiers modifiers); - - template - mouse_event(QtLikeEvent *evt) : position(evt->pos()) { - if (evt->buttons() == 1) { - modifiers |= left_key; - } else if (evt->buttons() == 2) { - modifiers |= right_key; - } - - if (evt->modifiers() == 0x04000000) { - modifiers |= ctrl_key; - }; - } - - bool is(key_modifiers mod) const; - bool with(key_modifiers mod) const; - - friend std::ostream &operator<<(std::ostream &os, const mouse_event &e); - - point position; - key_modifiers modifiers = 0; - }; - - struct mousehandler_parameters { - int stretch_zone_height = 5; - }; - - class mousehandler { - public: - enum class cursor { - pointer, - closed_hand, - open_hand, - resize - }; - - struct range { - int top = 0; - int bottom = 0; - - bool contains(int coord) { - return coord >= top && coord <= bottom; - } - - friend std::ostream &operator<<(std::ostream &os, const range &range); - }; - - std::function on_cursor_change = nullptr; - std::function on_set_selected = nullptr; - - explicit mousehandler(stg::strategy &strategy, - std::function slot_height_getter, - std::function bounds_getter, - const mousehandler_parameters &settings = mousehandler_parameters()); - - ~mousehandler(); - - void mouse_press(const mouse_event &event); - void mouse_move(const mouse_event &event); - void mouse_release(const mouse_event &event); - - void update_cursor(const mouse_event &event); - private: - enum class mouse_zone { - out_of_bounds = 0, - drag, - stretch_top, - stretch_bottom - }; - - enum class direction { - up, - down, - }; - - enum operation_type { - none = 0, - drag, - stretch_top, - stretch_bottom, - select - }; - - struct operation; - struct none_operation; - struct drag_operation; - struct stretch_top_operation; - struct stretch_bottom_operation; - struct select_operation; - - using index_t = stg::strategy::time_slot_index_t; - - std::function get_bounds = nullptr; - std::function get_slot_height = nullptr; - - stg::strategy &strategy; - mousehandler_parameters settings; - - cursor current_cursor = cursor::pointer; - std::unique_ptr current_operaion; - - float px_in_time(); - - index_t get_slot_index(const mouse_event &event); - index_t get_session_index(index_t slot_index); - range get_session_range(const mouse_event &event); - mouse_zone get_mouse_zone(mouse_event event); - cursor get_cursor(const mouse_event &event); - std::unique_ptr get_operation(const mouse_event &event); - - void set_selected(bool selected); - - friend std::ostream &operator<<(std::ostream &os, mouse_zone zone); - }; - -} - - -#endif //STRATEGR_MOUSEHANDLER_H diff --git a/models/mousehandleroperations.h b/models/mousehandleroperations.h index eb26cbc..5e2b626 100644 --- a/models/mousehandleroperations.h +++ b/models/mousehandleroperations.h @@ -5,105 +5,343 @@ #ifndef STRATEGR_MOUSEHANDLEROPERATIONS_H #define STRATEGR_MOUSEHANDLEROPERATIONS_H -#include "mousehandler.h" +#include "mouse_handler.h" -namespace stg { +struct stg::mouse_handler::operation { + explicit operation(mouse_handler *handler) : handler(*handler) {} - struct mousehandler::operation { - explicit operation(mousehandler *handler) : handler(handler) {} + virtual ~operation() = default; - virtual ~operation() = default; + virtual operation_type type() = 0; - virtual operation_type type() = 0; + virtual void init(const mouse_event &event) = 0; - virtual void init(const mouse_event &event) = 0; + virtual void perform(const mouse_event &event) = 0; - virtual void perform(const mouse_event &event) = 0; + virtual void teardown(const mouse_event &event) = 0; +protected: + mouse_handler &handler; + stg::strategy &strategy = handler.strategy; +}; - virtual void teardown(const mouse_event &event) = 0; +struct stg::mouse_handler::none_operation : public operation { + using operation::operation; -// friend bool operator==(operation *lhs, operation rhs) { -// return lhs->type() == rhs.type(); -// } + operation_type type() override { + return none; + } -// friend bool operator!=(operation *lhs, operation rhs) { -// return lhs->type() != rhs.type(); -// } + void init(const mouse_event &event) override {}; - protected: - mousehandler *handler; + void perform(const mouse_event &event) override {}; + + void teardown(const mouse_event &event) override {}; +}; + +struct stg::mouse_handler::drag_operation : public operation { + using operation::operation; + + operation_type type() override { + return drag; + } + + void init(const mouse_event &event) override { + handler.selection.deselect_all(); + + if (handler.on_select_sessions) + handler.on_select_sessions({handler.current_session_index}); + + strategy.begin_dragging(handler.current_session_index); + + previous_session_index = handler.current_session_index; + previous_slot_index = handler.current_slot_index; + previous_position = event.position; }; - struct mousehandler::none_operation : public operation { - using operation::operation; + void perform(const mouse_event &event) override { + auto current_session = strategy.sessions()[handler.current_session_index]; + auto session_slots_indices = strategy.sessions() + .get_bounds_for(previous_session_index); - operation_type type() override { - return none; + auto boundary_delta = get_delta(previous_position.y, event.position.y); + if (boundary_delta < handler.get_slot_height() / 2) { + return; } - void init(const mouse_event &event) override {}; + int distance = handler.current_slot_index - previous_slot_index; - void perform(const mouse_event &event) override {}; + if (!distance) + return; - void teardown(const mouse_event &event) override {}; + strategy.drag_session(previous_session_index, distance); + + previous_session_index = strategy.sessions() + .session_index_for_time_slot_index(handler.current_slot_index); + previous_slot_index = handler.current_slot_index; + previous_position = event.position; + + if (handler.on_select_sessions) + handler.on_select_sessions({previous_session_index}); }; + void teardown(const mouse_event &event) override { + strategy.end_dragging(); - struct mousehandler::drag_operation : public operation { - using operation::operation; + if (handler.on_select_sessions) + handler.on_select_sessions({}); + }; +private: + point previous_position; - operation_type type() override { - return drag; - } + index_t previous_slot_index = -1; + index_t previous_session_index = -1; - void init(const mouse_event &event) override {}; + int get_delta(int prev_pos, int current_pos) { + auto slot_height = handler.get_slot_height(); + auto closest_slot_boundary = std::round(prev_pos / slot_height) * slot_height; - void perform(const mouse_event &event) override {}; + return std::abs(static_cast(current_pos - closest_slot_boundary)); + } +}; - void teardown(const mouse_event &event) override {}; +struct stg::mouse_handler::resize_operation : public operation { + using operation::operation; + static constexpr auto nothing_selected_index = -2; + + enum direction { + none, + up, + down, }; - struct mousehandler::stretch_top_operation : public operation { - using operation::operation; + operation_type type() override { + return resize; + } + + void init(const mouse_event &event) override { + handler.selection.deselect_all(); + + handle_index = handler.current_slot_index; + initial_mouse_zone = handler.current_mouse_zone; + initial_position = event.position; - operation_type type() override { - return stretch_top; + select_sessions(handle_index, initial_mouse_zone, none); + + strategy.begin_resizing(); + } + + void perform(const mouse_event &event) override { + if (finished) + return; + + auto boundary_delta = get_boundary_delta(initial_position.y, event.position.y); + if (boundary_delta < handler.get_slot_height() / 2) { + return; + } + + auto direction = get_direction(event.position.y - get_boundary_position()); + + if (previous_direction != none && + direction != none && + direction != previous_direction) { + handle_direction_change(); } - void init(const mouse_event &event) override {}; + if (direction == none) + return; + + previous_direction = direction; + + index_t source_slot_index = get_source_slot_index(direction, initial_mouse_zone); + if (source_slot_index == handler.current_slot_index) { + return; + } + + if (!strategy.time_slots()[source_slot_index].activity && + !strategy.time_slots()[handler.current_slot_index].activity) { + + teardown(mouse_event(point(), 0)); + return; + } + + strategy.fill_time_slots(source_slot_index, handler.current_slot_index); + + auto new_mouse_zone = direction == down + ? mouse_zone::stretch_bottom + : mouse_zone::stretch_top; + + handle_index = handler.current_slot_index; + initial_position = event.position; + initial_mouse_zone = new_mouse_zone; - void perform(const mouse_event &event) override {}; + select_sessions(handle_index, initial_mouse_zone, direction); + } - void teardown(const mouse_event &event) override {}; + void teardown(const mouse_event &event) + override { + if (handler.on_select_sessions) + handler.on_select_sessions({}); + + if (handler.on_resize_boundary_change) + handler.on_resize_boundary_change(nothing_selected_index, + nothing_selected_index); + + strategy.end_resizing(); + finished = true; }; - struct mousehandler::stretch_bottom_operation : public operation { - using operation::operation; +private: + bool finished = false; + + index_t handle_index = -1; + index_t boundary_slot_index = -1; + + point initial_position; + mouse_zone initial_mouse_zone = mouse_zone::out_of_bounds; + + direction previous_direction = none; + + static direction get_direction(int delta) { + if (delta == 0) + return none; + return delta > 0 ? down : up; + } + + int get_boundary_delta(int prev_pos, int current_pos) { + auto boundary_pos = get_boundary_position(); + auto true_delta = static_cast(current_pos - boundary_pos); + + // "-" is a defence against cursor trembling + return std::abs(true_delta) - static_cast(handler.settings.resolution); + } + + int get_boundary_position() const { + return handler.get_slot_height() * (boundary_slot_index + 1); + } + + void handle_direction_change() { + if (initial_mouse_zone == mouse_zone::stretch_top && + previous_direction == up) { + if (handle_index != boundary_slot_index + 1) + handle_index = boundary_slot_index + 1; + } + + if (initial_mouse_zone == mouse_zone::stretch_bottom && + previous_direction == up) { + if (handle_index != boundary_slot_index) + handle_index = boundary_slot_index; + } + } + + void select_sessions(index_t source_index, + enum mouse_zone mouse_zone, + direction direction) { + auto first_selection_index = strategy.sessions() + .session_index_for_time_slot_index(source_index); + + if (mouse_zone == mouse_zone::stretch_top && + direction == direction::down) { + first_selection_index++; + } + + if (mouse_zone == mouse_zone::stretch_bottom && + direction == direction::up) { + first_selection_index--; + } + + auto second_selection_index = first_selection_index + 1; + if (mouse_zone == mouse_zone::stretch_top) { + second_selection_index = first_selection_index - 1; + } - operation_type type() override { - return stretch_bottom; + auto first_session_index = std::min(first_selection_index, second_selection_index); + if (first_session_index < 0) { + boundary_slot_index = -1; + } else { + auto border_slot = strategy.sessions()[first_session_index].time_slots.back(); + boundary_slot_index = strategy.time_slots().index_of(border_slot).value_or(-1); } - void init(const mouse_event &event) override {}; + if (handler.on_select_sessions) { + auto selected = get_selected(first_selection_index, second_selection_index); + handler.on_select_sessions(selected); + } - void perform(const mouse_event &event) override {}; + if (handler.on_resize_boundary_change) + handler.on_resize_boundary_change(first_session_index, boundary_slot_index); + } + + std::vector get_selected(index_t first_selection_index, index_t second_selection_index) const { + std::__1::vector selected; + if (strategy.sessions().has_index(first_selection_index)) + selected.push_back(first_selection_index); + + if (strategy.sessions().has_index(second_selection_index)) + selected.push_back(second_selection_index); + return selected; + } + + index_t get_source_slot_index(const direction &direction, mouse_zone mouse_zone) const { + auto source_slot_index = handle_index; + if (mouse_zone == mouse_zone::stretch_top && + direction == down) { + source_slot_index--; + } else if (mouse_zone == mouse_zone::stretch_bottom && + direction == up) { + source_slot_index++; + } - void teardown(const mouse_event &event) override {}; + return source_slot_index; }; +}; - struct mousehandler::select_operation : public operation { - using operation::operation; +struct stg::mouse_handler::select_operation : public operation { + using operation::operation; - operation_type type() override { - return select; + operation_type type() override { + return select; + } + + void init(const mouse_event &event) override { + initial_event = std::make_unique(event); + + if (current_slot_is_selected() && !event.with(event::ctrl_key)) { + handler.selection.set_is_clicked(true); + return; } - void init(const mouse_event &event) override {}; + handler.selection.toggle_at(handler.current_slot_index); + }; - void perform(const mouse_event &event) override {}; + void perform(const mouse_event &event) override { + if (current_slot_is_selected()) { + return; + } - void teardown(const mouse_event &event) override {}; + handler.selection.toggle_at(handler.current_slot_index); }; -} + + void teardown(const mouse_event &event) override { + if (handler.selection.is_clicked()) { + handler.selection.set_is_clicked(false); + + if (initial_event->with(stg::event::left_key)) { + if (handler.on_open_activities) + handler.on_open_activities(); + + } else if (initial_event->with(stg::event::right_key)) { + if (handler.on_show_context_menu) + handler.on_show_context_menu(event.position); + } + } + }; + +private: + std::unique_ptr initial_event; + + bool current_slot_is_selected() { + return handler.selection.has_selected(handler.current_slot_index); + } +}; #endif //STRATEGR_MOUSEHANDLEROPERATIONS_H diff --git a/models/notifiableonchange.h b/models/notifiableonchange.h index 7141280..47d8ea2 100644 --- a/models/notifiableonchange.h +++ b/models/notifiableonchange.h @@ -30,7 +30,7 @@ namespace stg { protected: mutable std::vector on_change_callbacks = {}; - void on_change_event() { + virtual void on_change_event() { for (const auto &callback: on_change_callbacks) { callback(); } diff --git a/models/selection.cpp b/models/selection.cpp new file mode 100644 index 0000000..3e50f70 --- /dev/null +++ b/models/selection.cpp @@ -0,0 +1,111 @@ +// +// Created by Dmitry Khrykin on 2020-01-30. +// + +#include "selection.h" + +stg::selection::selection(const stg::strategy &strategy) + : strategy(strategy) {} + +void stg::selection::toggle_at(index_t slot_index) { + auto found_it = std::find(begin(), end(), slot_index); + auto already_selected = found_it != end(); + + if (already_selected) { + erase(found_it); + } else { + push_back(slot_index); + std::sort(begin(), end()); + } + + on_change_event(); +} + +void stg::selection::deselect_all() { + clear(); + _is_clicked = false; + + on_change_event(); +} + +void stg::selection::select_all() { + resize(strategy.number_of_time_slots()); + + std::iota(begin(), end(), 0); + + on_change_event(); +} + +void stg::selection::fill(index_t from_index, index_t to_index) { + if (from_index > to_index) { + std::swap(from_index, to_index); + } + + if (from_index <= 0) { + from_index = 0; + } else if (to_index >= strategy.number_of_time_slots()) { + from_index = strategy.number_of_time_slots() - 1; + } + + for (auto i = from_index; i <= to_index; i++) { + if (!has_selected(i)) + push_back(i); + } + + std::sort(begin(), end()); + + on_change_event(); +} + +bool stg::selection::is_continuous() const { + return size() == 1; +} + +bool stg::selection::has_selected(index_t slot_index) { + return std::find(begin(), end(), slot_index) != end(); +} + +void stg::selection::reload() { + grouped_selection new_grouped; + grouped_selection_element current_item; + + for (auto i = begin(); i != end(); ++i) { + auto current_index = *i; + auto previous = i != begin() + ? std::make_optional(*prev(i)) + : std::nullopt; + + if (!previous || current_index == *previous + 1) { + current_item.push_back(current_index); + } else { + new_grouped.push_back(current_item); + current_item = {current_index}; + } + + if (i == prev(end())) { + new_grouped.push_back(current_item); + } + } + + _grouped = new_grouped; + + notifiable_on_change::on_change_event(); +} + +bool stg::selection::is_clicked() const { + return _is_clicked; +} + +void stg::selection::set_is_clicked(bool is_clicked) { + _is_clicked = is_clicked; + + on_change_event(); +} + +void stg::selection::on_change_event() { + reload(); +} + +const stg::grouped_selection &stg::selection::grouped() { + return _grouped; +} diff --git a/models/selection.h b/models/selection.h new file mode 100644 index 0000000..e87ec6a --- /dev/null +++ b/models/selection.h @@ -0,0 +1,52 @@ +// +// Created by Dmitry Khrykin on 2020-01-30. +// + +#ifndef STRATEGR_SELECTION_H +#define STRATEGR_SELECTION_H + +#include +#include +#include + +#include "strategy.h" +#include "notifiableonchange.h" + +namespace stg { + using index_t = strategy::time_slot_index_t; + using grouped_selection_element = std::vector; + using grouped_selection = std::vector; + + struct selection : public std::vector, + public notifiable_on_change { + explicit selection(const stg::strategy &strategy); + + void toggle_at(index_t slot_index); + void deselect_all(); + void select_all(); + void fill(index_t from_index, index_t to_index); + + bool is_continuous() const; + + bool has_selected(index_t slot_index); + + void reload(); + + bool is_clicked() const; + void set_is_clicked(bool is_clicked); + + const grouped_selection &grouped(); + + void on_change_event() override; + private: + const stg::strategy &strategy; + + grouped_selection _grouped; + + std::function get_slot_height; + + bool _is_clicked = false; + }; +} + +#endif //STRATEGR_SELECTION_H diff --git a/models/sessionslist.cpp b/models/sessionslist.cpp index 35f3c1f..f277766 100644 --- a/models/sessionslist.cpp +++ b/models/sessionslist.cpp @@ -92,3 +92,7 @@ std::vector stg::sessions_list::get_non_empty() const { return result; } + +stg::time_slot::time_t stg::sessions_list::relative_time(const stg::session &session) const { + return session.begin_time() - _data.front().begin_time(); +} diff --git a/models/sessionslist.h b/models/sessionslist.h index c184f3c..8ffdcc0 100644 --- a/models/sessionslist.h +++ b/models/sessionslist.h @@ -29,9 +29,29 @@ namespace stg { std::optional color = std::nullopt; }; + struct bounds { + index_t start_index = 0; + index_t end_index = 0; + + bool contain(index_t index) { + return index >= start_index && index <= end_index; + } + }; + std::vector get_non_empty() const; - std::string class_print_name() const override; + bounds get_bounds_for(index_t session_index) const { + auto &session = _data[session_index]; + + auto global_begin_time = _data.front().begin_time(); + auto total_duration = _data.back().end_time() - _data.front().begin_time(); + auto slot_duration = _data.front().duration(); + + index_t start_index = static_cast(session.begin_time() - global_begin_time) / slot_duration; + index_t end_index = start_index + session.length(); + + return {start_index, end_index}; + } stg::sessions_list::index_t session_index_for_time_slot_index(index_t time_slot_index) const; @@ -42,10 +62,9 @@ namespace stg { std::vector overview() const; - time_slot::time_t relative_time(const session &session) const { - return session.begin_time() - _data.front().begin_time(); - } + time_slot::time_t relative_time(const session &session) const; + std::string class_print_name() const override; private: using activity_sessions_list_base::activity_sessions_list_base; diff --git a/models/strategy.cpp b/models/strategy.cpp index 87ee534..8c91543 100644 --- a/models/strategy.cpp +++ b/models/strategy.cpp @@ -134,9 +134,8 @@ void stg::strategy::drag_activity(activity_index from_index, activity_index to_i } void stg::strategy::fill_time_slots(time_slot_index_t from_index, time_slot_index_t till_index) { - if (!current_resize_operation) { - return; - } + assert(current_resize_operation && "fill_time_slots must be called between " + "begin_resizing() and end_resizing() calls"); current_resize_operation->fill_slots(from_index, till_index); } @@ -200,11 +199,11 @@ void stg::strategy::shift_below_time_slot(stg::strategy::time_slot_index_t from_ commit_to_history(); } -void stg::strategy::drag_activity_session(stg::strategy::session_index_t session_index, - int distance) { - if (!current_drag_operation) { - return; - } +void stg::strategy::drag_session(stg::strategy::session_index_t session_index, + int distance) { + assert(current_drag_operation && "drag_session must be called between " + "begin_dragging() and end_dragging() calls"); + if (session_index < 0 || session_index > sessions().size() - 1 || diff --git a/models/strategy.h b/models/strategy.h index 821222f..8ea128a 100644 --- a/models/strategy.h +++ b/models/strategy.h @@ -51,7 +51,6 @@ namespace stg { const sessions_list &sessions() const; const time_slots_state &time_slots() const; - time_t begin_time() const; void set_begin_time(time_t begin_time); @@ -83,7 +82,7 @@ namespace stg { void end_resizing(); void begin_dragging(session_index_t session_index); - void drag_activity_session(session_index_t session_index, int distance); + void drag_session(session_index_t session_index, int distance); void end_dragging(); void shift_below_time_slot(time_slot_index_t from_index, int length); diff --git a/models/tests/strategy_timeslots_test.cpp b/models/tests/strategy_timeslots_test.cpp index a47b670..8cc90ba 100644 --- a/models/tests/strategy_timeslots_test.cpp +++ b/models/tests/strategy_timeslots_test.cpp @@ -122,7 +122,7 @@ TEST_CASE("Strategy activity sessions", "[strategy][sessions]") { SECTION("up") { strategy.begin_dragging(2); - strategy.drag_activity_session(2, -2); + strategy.drag_session(2, -2); strategy.end_dragging(); REQUIRE(strategy.sessions()[0].activity == stg::strategy::no_activity); @@ -137,7 +137,7 @@ TEST_CASE("Strategy activity sessions", "[strategy][sessions]") { SECTION("down") { strategy.begin_dragging(1); - strategy.drag_activity_session(1, 2); + strategy.drag_session(1, 2); strategy.end_dragging(); REQUIRE(strategy.sessions()[0].activity == stg::strategy::no_activity); diff --git a/models/timer.cpp b/models/timer.cpp index a610733..fa74135 100644 --- a/models/timer.cpp +++ b/models/timer.cpp @@ -6,6 +6,7 @@ stg::timer::~timer() { stop(); + if (timer_thread.joinable()) timer_thread.join(); } diff --git a/models/timeslotsstate.cpp b/models/timeslotsstate.cpp index 9901191..db114a9 100644 --- a/models/timeslotsstate.cpp +++ b/models/timeslotsstate.cpp @@ -78,18 +78,26 @@ stg::time_slots_state::time_slots_state(std::vector from_vector) { } void stg::time_slots_state::fill_slots(index_t from_index, index_t till_index) { - if (from_index == till_index || !has_indices(from_index, till_index)) { - return; - } - auto source_index = from_index; if (till_index < from_index) { std::swap(till_index, from_index); } + if (till_index >= size() - 1) { + till_index = size() - 1; + } else if (from_index < 0) { + from_index = 0; + } else if (till_index < 0 || from_index >= size() - 1) { + return; + } + + auto new_activity = has_index(source_index) + ? _data[source_index].activity + : time_slot::no_activity; + for (auto copy_index = from_index; copy_index <= till_index; copy_index++) { - _data[copy_index].activity = _data[source_index].activity; + _data[copy_index].activity = new_activity; } on_change_event(); diff --git a/ui/activitylistwidget.cpp b/ui/activitylistwidget.cpp index b411331..fde0306 100644 --- a/ui/activitylistwidget.cpp +++ b/ui/activitylistwidget.cpp @@ -11,9 +11,9 @@ #include "activityinvalidpropertyexception.h" #include "activitylistwidget.h" -#include "activitylistitemwidget.h" +#include "activitywidget.h" #include "mainscene.h" -#include "slotboard.h" +#include "slotboardwidget.h" #include "searchbox.h" ActivityListWidget::ActivityListWidget(stg::strategy &strategy, @@ -123,7 +123,7 @@ void ActivityListWidget::layoutChildWidgets() { } void ActivityListWidget::setSelectedForItemAtIndex(int index, bool isSelected) const { - auto listItem = qobject_cast( + auto listItem = qobject_cast( listWidget->layout()->itemAt(index)->widget()); if (listItem) @@ -138,7 +138,6 @@ void ActivityListWidget::deselectAllItems() { selectedActivityIndex = -1; } - void ActivityListWidget::setupNavbar() { navbar = new Navbar(); layout()->addWidget(navbar); @@ -170,7 +169,7 @@ void ActivityListWidget::getBack() { } void ActivityListWidget::reconnectItemAtIndex(int itemIndex, - ActivityListItemWidget *item) { + ActivityWidget *item) { item->disconnect(); auto activityIndex = itemIndex; @@ -178,38 +177,35 @@ void ActivityListWidget::reconnectItemAtIndex(int itemIndex, activityIndex = *strategy.activities().index_of(searchResults.at(itemIndex)); } - connect(item, &ActivityListItemWidget::selected, - [=]() { - if (mainScene()->selection().empty()) - return; + connect(item, &ActivityWidget::selected, [=] { + strategy.place_activity(activityIndex, mainScene()->selection()); + mainScene()->selection().deselect_all(); - strategy.place_activity(activityIndex, - mainScene()->selection()); - getBack(); - }); + getBack(); + }); - connect(item, &ActivityListItemWidget::activityDeleted, - [=]() { - strategy.delete_activity(activityIndex); - if (isShowingSearchResults()) { - performSearch(); - } - }); + connect(item, &ActivityWidget::activityDeleted, [=] { + strategy.delete_activity(activityIndex); - connect(item, &ActivityListItemWidget::activityEdited, - [=](const stg::activity &newActivity) { - strategy.edit_activity(activityIndex, newActivity); - if (isShowingSearchResults()) { - performSearch(); - } - }); + if (isShowingSearchResults()) { + performSearch(); + } + }); + + connect(item, &ActivityWidget::activityEdited, [=](const stg::activity &newActivity) { + strategy.edit_activity(activityIndex, newActivity); + + if (isShowingSearchResults()) { + performSearch(); + } + }); - connect(item, &ActivityListItemWidget::hovered, [=]() { + connect(item, &ActivityWidget::hovered, [=] { deselectAllItems(); removeBorderBeforeIndex(itemIndex); }); - connect(item, &ActivityListItemWidget::unhovered, [=]() { + connect(item, &ActivityWidget::unhovered, [=] { removeBorderBeforeIndex(0); }); } @@ -257,7 +253,7 @@ QVBoxLayout *ActivityListWidget::listLayout() { return qobject_cast(listWidget->layout()); } -void ActivityListWidget::reuseItemAtIndex(int index, ActivityListItemWidget *itemWidget) { +void ActivityListWidget::reuseItemAtIndex(int index, ActivityWidget *itemWidget) { auto activity = isShowingSearchResults() ? searchResults.at(index) : strategy.activities().at(index); @@ -265,12 +261,12 @@ void ActivityListWidget::reuseItemAtIndex(int index, ActivityListItemWidget *ite reconnectItemAtIndex(index, itemWidget); } -ActivityListItemWidget *ActivityListWidget::makeNewItemAtIndex(int index) { +ActivityWidget *ActivityListWidget::makeNewItemAtIndex(int index) { auto activity = isShowingSearchResults() ? searchResults.at(index) : strategy.activities().at(index); - auto itemWidget = new ActivityListItemWidget(activity); + auto itemWidget = new ActivityWidget(activity); reconnectItemAtIndex(index, itemWidget); return itemWidget; } @@ -280,7 +276,7 @@ void ActivityListWidget::removeBorderBeforeIndex(int index) { if (!listLayout()->itemAt(i)->widget()) continue; - auto itemWidget = qobject_cast(listLayout()->itemAt(i)->widget()); + auto itemWidget = qobject_cast(listLayout()->itemAt(i)->widget()); if (!itemWidget) continue; @@ -310,8 +306,8 @@ bool ActivityListWidget::eventFilter(QObject *object, QEvent *event) { || keyEvent->type() == QEvent::KeyPress); if (object == this && isKeyPressEvent) { - if (mainScene()->selection().empty()) - return false; +// if (mainScene()->selection().empty()) +// return false; if (keyEvent->key() == Qt::Key_Down) { selectDown(); return true; @@ -322,7 +318,7 @@ bool ActivityListWidget::eventFilter(QObject *object, QEvent *event) { keyEvent->key() == Qt::Key_Enter) { if (selectedActivityIndex >= 0) { auto itemWidget = listItemWidgetAtIndex(selectedActivityIndex); - itemWidget->choose(); + itemWidget->choose(nullptr); } return true; diff --git a/ui/activitylistwidget.h b/ui/activitylistwidget.h index bcf33d2..0d9b45f 100644 --- a/ui/activitylistwidget.h +++ b/ui/activitylistwidget.h @@ -7,7 +7,7 @@ #include "navbar.h" #include "activityeditormenu.h" #include "third-party/slidingstackedwidget.h" -#include "activitylistitemwidget.h" +#include "activitywidget.h" #include "reactivelist.hpp" #include "colorprovider.h" #include "coloredlabel.h" @@ -19,7 +19,7 @@ class QLineEdit; class QScrollArea; class ActivityListWidget : public QWidget, - public ReactiveList, + public ReactiveList, public ColorProvider { Q_OBJECT public: @@ -71,9 +71,9 @@ Q_OBJECT // ReactiveList int numberOfItems() override; QVBoxLayout *listLayout() override; - void reuseItemAtIndex(int index, ActivityListItemWidget *itemWidget) override; - ActivityListItemWidget *makeNewItemAtIndex(int index) override; - void reconnectItemAtIndex(int itemIndex, ActivityListItemWidget *item); + void reuseItemAtIndex(int index, ActivityWidget *itemWidget) override; + ActivityWidget *makeNewItemAtIndex(int index) override; + void reconnectItemAtIndex(int itemIndex, ActivityWidget *item); void scrollUpItemIntoViewAtIndex(int index); diff --git a/ui/activitylistitemwidget.cpp b/ui/activitywidget.cpp similarity index 60% rename from ui/activitylistitemwidget.cpp rename to ui/activitywidget.cpp index 2669155..d4cf9ec 100644 --- a/ui/activitylistitemwidget.cpp +++ b/ui/activitywidget.cpp @@ -7,13 +7,14 @@ #include #include -#include "activitylistitemwidget.h" +#include "activitywidget.h" #include "utils.h" #include "colorutils.h" #include "applicationsettings.h" +#include "mainwindow.h" -ActivityListItemWidget::ActivityListItemWidget(stg::activity *activity, - QWidget *parent) +ActivityWidget::ActivityWidget(stg::activity *activity, + QWidget *parent) : QWidget(parent), _activity(activity) { setFixedHeight(ApplicationSettings::defaultActivityItemHeight); @@ -28,19 +29,20 @@ ActivityListItemWidget::ActivityListItemWidget(stg::activity *activity, updateUI(); } -void ActivityListItemWidget::setupActions() { +void ActivityWidget::setupActions() { } -void ActivityListItemWidget::layoutChildWidgets() { +void ActivityWidget::layoutChildWidgets() { label = new ColoredLabel(); label->setAlignment(Qt::AlignCenter); label->setBold(true); + label->setFontHeight(ApplicationSettings::sessionFontSize); label->setElideMode(Qt::ElideMiddle); layout()->addWidget(label); } -void ActivityListItemWidget::paintEvent(QPaintEvent *) { +void ActivityWidget::paintEvent(QPaintEvent *) { auto painter = QPainter(this); painter.setPen(Qt::NoPen); @@ -55,7 +57,7 @@ void ActivityListItemWidget::paintEvent(QPaintEvent *) { } } -void ActivityListItemWidget::drawBorder(QPainter &painter) const { +void ActivityWidget::drawBorder(QPainter &painter) const { painter.setBrush(borderColor()); auto borderRect = QRect(8, height() - 1, @@ -65,7 +67,7 @@ void ActivityListItemWidget::drawBorder(QPainter &painter) const { painter.drawRect(borderRect); } -void ActivityListItemWidget::drawSelection(QPainter &painter) const { +void ActivityWidget::drawSelection(QPainter &painter) const { painter.setBrush(_isSelected ? selectionColor() : highlightColor()); auto selectionRect = QRect(0, 0, width(), height()); @@ -73,7 +75,7 @@ void ActivityListItemWidget::drawSelection(QPainter &painter) const { painter.drawRect(selectionRect); } -void ActivityListItemWidget::updateUI() { +void ActivityWidget::updateUI() { using namespace ColorUtils; label->setText(QString::fromStdString(activity()->name())); label->setDynamicColor([this]() { @@ -81,18 +83,18 @@ void ActivityListItemWidget::updateUI() { }); } -stg::activity *ActivityListItemWidget::activity() const { +stg::activity *ActivityWidget::activity() const { return _activity; } -void ActivityListItemWidget::setActivity(stg::activity *activity) { +void ActivityWidget::setActivity(stg::activity *activity) { if (activity != _activity) { _activity = activity; updateUI(); } } -void ActivityListItemWidget::mousePressEvent(QMouseEvent *event) { +void ActivityWidget::mousePressEvent(QMouseEvent *event) { if (event->buttons() == Qt::LeftButton) { isClicked = true; update(); @@ -100,18 +102,20 @@ void ActivityListItemWidget::mousePressEvent(QMouseEvent *event) { } } -void ActivityListItemWidget::mouseReleaseEvent(QMouseEvent *event) { +void ActivityWidget::mouseReleaseEvent(QMouseEvent *event) { auto wasClicked = isClicked; - isClicked = false; - update(); - emit unhovered(); if (wasClicked) - emit selected(); + choose(event); +} + +void ActivityWidget::contextMenuEvent(QContextMenuEvent *event) { + showContextMenu(event->pos()); } -void ActivityListItemWidget::contextMenuEvent(QContextMenuEvent *event) { +void ActivityWidget::showContextMenu(const QPoint &position) { + isClicked = true; update(); auto newEditorMenu = new ActivityEditorMenu(*activity(), this); @@ -130,38 +134,55 @@ void ActivityListItemWidget::contextMenuEvent(QContextMenuEvent *event) { emit activityDeleted(); }); + connect(editorMenu, + &ActivityEditorMenu::aboutToHide, + [=]() { + isClicked = false; + update(); + }); + + editorMenu->focus(); - editorMenu->exec(mapToGlobal(event->pos())); + editorMenu->exec(mapToGlobal(position)); } -void ActivityListItemWidget::enterEvent(QEvent *event) { +void ActivityWidget::enterEvent(QEvent *event) { QWidget::enterEvent(event); update(); } -void ActivityListItemWidget::leaveEvent(QEvent *event) { +void ActivityWidget::leaveEvent(QEvent *event) { QWidget::leaveEvent(event); update(); } -bool ActivityListItemWidget::drawsBorder() const { +bool ActivityWidget::drawsBorder() const { return _drawsBorder; } -void ActivityListItemWidget::setDrawsBorder(bool drawsBorder) { +void ActivityWidget::setDrawsBorder(bool drawsBorder) { _drawsBorder = drawsBorder; update(); } -bool ActivityListItemWidget::isSelected() const { +bool ActivityWidget::isSelected() const { return _isSelected; } -void ActivityListItemWidget::setIsSelected(bool isSelected) { +void ActivityWidget::setIsSelected(bool isSelected) { _isSelected = isSelected; update(); } -void ActivityListItemWidget::choose() { - emit selected(); +void ActivityWidget::choose(QMouseEvent *event) { + auto mainScene = qobject_cast(window())->scene(); + + if (mainScene->selection().empty()) { + auto pos = event ? event->pos() : contentsRect().center(); + showContextMenu(pos); + } else { + emit selected(); + isClicked = false; + update(); + } } diff --git a/ui/activitylistitemwidget.h b/ui/activitywidget.h similarity index 84% rename from ui/activitylistitemwidget.h rename to ui/activitywidget.h index 4b5ca3a..07af57d 100644 --- a/ui/activitylistitemwidget.h +++ b/ui/activitywidget.h @@ -13,11 +13,11 @@ #include "colorprovider.h" #include "coloredlabel.h" -class ActivityListItemWidget : public QWidget, public ColorProvider { +class ActivityWidget : public QWidget, public ColorProvider { Q_OBJECT public: - explicit ActivityListItemWidget(stg::activity *activity, - QWidget *parent = nullptr); + explicit ActivityWidget(stg::activity *activity, + QWidget *parent = nullptr); stg::activity *activity() const; void setActivity(stg::activity *activity); @@ -26,8 +26,7 @@ Q_OBJECT bool isSelected() const; void setIsSelected(bool isSelected); - void choose(); - + void choose(QMouseEvent *event = nullptr); signals: void hovered(); void unhovered(); @@ -60,6 +59,7 @@ Q_OBJECT void setupActions(); void drawSelection(QPainter &painter) const; void drawBorder(QPainter &painter) const; + void showContextMenu(const QPoint &position); }; #endif // ACTIVITYLISTITEMWIDGET_H diff --git a/ui/coloredlabel.cpp b/ui/coloredlabel.cpp index 3f1b5ad..26ef197 100644 --- a/ui/coloredlabel.cpp +++ b/ui/coloredlabel.cpp @@ -55,7 +55,7 @@ void ColoredLabel::paintEvent(QPaintEvent *event) { auto textRect = contentsRect(); auto printedText = _elideMode == Qt::ElideNone ? _text - : fontMetrics().elidedText(_text, Qt::ElideMiddle, contentsRect().width()); + : fontMetrics().elidedText(_text, Qt::ElideMiddle, textRect.width()); painter.drawText(textRect, _alignment, printedText); } diff --git a/ui/currentsessionwidget.cpp b/ui/currentsessionwidget.cpp index 49a0605..2cd3d56 100644 --- a/ui/currentsessionwidget.cpp +++ b/ui/currentsessionwidget.cpp @@ -40,26 +40,21 @@ CurrentSessionWidget::CurrentSessionWidget(stg::strategy &strategy, QWidget *par currentLabel->setFontHeight(10); currentLabel->setBold(true); currentLabel->setDynamicColor(&ColorProvider::tertiaryTextColor); - - currentLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); + currentLabel->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); activityLabel = new ColoredLabel(); activityLabel->setBold(true); - activityLabel->setFontHeight(14); + activityLabel->setFontHeight(ApplicationSettings::sessionFontSize - 1); + activityLabel->setContentsMargins(0, 0, 0, 3); activityLabel->customRenderer = [&](QPainter *painter, const QString &) { auto activeSession = strategy.active_session(); if (!activeSession) { return; } - auto textRect = QRect(0, - 0, - activityLabel->width(), - activityLabel->height()); - FontUtils::drawSessionTitle(*activeSession, *painter, - textRect); + activityLabel->contentsRect()); }; activityLabel->setAlignment(Qt::AlignTop | Qt::AlignHCenter); diff --git a/ui/mainscene.cpp b/ui/mainscene.cpp index 9055b8e..26a7e64 100644 --- a/ui/mainscene.cpp +++ b/ui/mainscene.cpp @@ -63,9 +63,6 @@ void MainScene::clearSelection() { sessionsMainWidget->clearSelection(); } -const SelectionWidget::RawSelectionState &MainScene::selection() { - return sessionsMainWidget->selection(); -} void MainScene::showNewActivityMenu() { activitiesWidget->showNewActivityMenu(); diff --git a/ui/mainscene.h b/ui/mainscene.h index 81185c9..9b6fcc4 100644 --- a/ui/mainscene.h +++ b/ui/mainscene.h @@ -25,10 +25,11 @@ Q_OBJECT void showNewActivityMenu(); - void clearSelection(); - - const SelectionWidget::RawSelectionState &selection(); + stg::selection &selection() { + return findChildren().first()->selection; + } + void clearSelection(); void reloadStrategy(); void reorderActivitiesByUsage(); diff --git a/ui/selectionwidget.cpp b/ui/selectionwidget.cpp index 56cd8cb..b0db5c9 100644 --- a/ui/selectionwidget.cpp +++ b/ui/selectionwidget.cpp @@ -11,119 +11,26 @@ SelectionWidget::SelectionWidget(stg::strategy &strategy, int slotHeight, QWidget *parent) - : strategy(strategy), QWidget(parent) { + : strategy(strategy), + selection(stg::selection{strategy}), + slotHeight(slotHeight), + QWidget(parent) { setMouseTracking(false); setAttribute(Qt::WA_TransparentForMouseEvents); -} - -SelectionWidget::SelectionState -SelectionWidget::makeSelectionState(RawSelectionState rawState) { - using namespace std; - SelectionState result; - RawSelectionState currentItem; - - for (auto i = rawState.begin(); i != rawState.end(); ++i) { - auto current = *i; - auto previous = i != rawState.begin() - ? make_optional(*prev(i)) - : nullopt; - - if (!previous || current == *previous + 1) { - currentItem.push_back(current); - } else { - result.push_back(currentItem); - currentItem = {current}; - } - - if (i == prev(rawState.end())) { - result.push_back(currentItem); - } - } - - return result; -} - -void SelectionWidget::selectAtIndex(int slotIndex) { - auto foundIt = std::find(rawSelectionState.begin(), - rawSelectionState.end(), - slotIndex); - - auto alreadySelected = foundIt != rawSelectionState.end(); - if (alreadySelected) { - rawSelectionState.erase(foundIt); - } else { - rawSelectionState.push_back(slotIndex); - std::sort(rawSelectionState.begin(), - rawSelectionState.end()); - } - - updateUI(); - emit selectionChanged(); + selection.add_on_change_callback([this] { + update(); + }); } -void SelectionWidget::setSlotHeight(int newSlotHeight) { - slotHeight = newSlotHeight; - updateUI(); -} - -void SelectionWidget::updateUI() { - selectionState = makeSelectionState(rawSelectionState); - - update(); -} - -void SelectionWidget::drawSelectionForItem(RawSelectionState &selectionItem, - QPainter &painter) { - auto topPosition = slotHeight * selectionItem.front(); - - auto widgetHeight = selectionItem.size() * slotHeight; - - const auto &lastTimeSlot = strategy.time_slots()[selectionItem.back()]; - auto bottomMargin = lastTimeSlot.end_time() % 60 == 0 ? 1 : 0; - - auto rect = QRect(contentsMargins().left(), - contentsMargins().top() + topPosition + 2, - width() - contentsMargins().right() - contentsMargins().left(), - widgetHeight - 5 - bottomMargin); - - painter.drawRoundedRect(rect, 4, 4); -} void SelectionWidget::resizeEvent(QResizeEvent *event) { - for (auto *child : children()) { - auto *widget = qobject_cast(child); - if (widget) { - widget->setFixedWidth(width()); - } - } -} - -void SelectionWidget::deselectAll() { - rawSelectionState = {}; - _isClicked = false; - updateUI(); - emit selectionChanged(); -} - -void SelectionWidget::selectAll(int numberOfSlots) { - rawSelectionState = RawSelectionState(numberOfSlots); - std::iota(rawSelectionState.begin(), rawSelectionState.end(), 0); - - updateUI(); - emit selectionChanged(); -} - -const SelectionWidget::RawSelectionState &SelectionWidget::selection() const { - return rawSelectionState; -} - -bool SelectionWidget::selectionIsContinuous() const { - return selectionState.size() == 1; -} - -void SelectionWidget::reloadStrategy() { - updateUI(); +// for (auto *child : children()) { +// auto *widget = qobject_cast(child); +// if (widget) { +// widget->setFixedWidth(width()); +// } +// } } void SelectionWidget::paintEvent(QPaintEvent *event) { @@ -137,50 +44,27 @@ void SelectionWidget::paintEvent(QPaintEvent *event) { auto clickedColor = selectionColor(); clickedColor.setAlphaF(clickedColor.alphaF() * 1.25); - painter.setBrush(_isClicked ? clickedColor : selectionColor()); + painter.setBrush(selection.is_clicked() + ? clickedColor + : selectionColor()); - for (auto &selectionItem : selectionState) { + for (auto &selectionItem : selection.grouped()) { drawSelectionForItem(selectionItem, painter); } } -bool SelectionWidget::isSlotIndexSelected(stg::strategy::time_slot_index_t slotIndex) { - return std::find(rawSelectionState.begin(), - rawSelectionState.end(), - slotIndex) != rawSelectionState.end(); -} - -bool SelectionWidget::isClicked() const { - return _isClicked; -} - -void SelectionWidget::setIsClicked(bool isClicked) { - _isClicked = isClicked; - - update(); -} - -void SelectionWidget::fillSelected(int fromIndex, int toIndex) { - - if (fromIndex > toIndex) { - std::swap(fromIndex, toIndex); - } - - if (fromIndex <= 0) { - fromIndex = 0; - } - - if (toIndex >= strategy.number_of_time_slots()) { - fromIndex = strategy.number_of_time_slots() - 1; - } +void SelectionWidget::drawSelectionForItem(const stg::grouped_selection_element &selectionItem, + QPainter &painter) { + auto topPosition = slotHeight * selectionItem.front(); + auto widgetHeight = static_cast(selectionItem.size()) * slotHeight; - for (auto i = fromIndex; i <= toIndex; i++) { - if (!isSlotIndexSelected(i)) - rawSelectionState.push_back(i); - } + const auto &lastTimeSlot = strategy.time_slots()[selectionItem.back()]; + auto bottomMargin = lastTimeSlot.end_time() % 60 == 0 ? 1 : 0; - std::sort(rawSelectionState.begin(), - rawSelectionState.end()); + auto rect = QRect(contentsMargins().left(), + contentsMargins().top() + topPosition + 2, + width() - contentsMargins().right() - contentsMargins().left(), + widgetHeight - 5 - bottomMargin); - updateUI(); -} + painter.drawRoundedRect(rect, 4, 4); +} \ No newline at end of file diff --git a/ui/selectionwidget.h b/ui/selectionwidget.h index d48417e..62a6b4c 100644 --- a/ui/selectionwidget.h +++ b/ui/selectionwidget.h @@ -10,47 +10,26 @@ #include "applicationsettings.h" #include "strategy.h" #include "colorprovider.h" +#include "selection.h" class SelectionWidget : public QWidget, public ColorProvider { Q_OBJECT public: - using RawSelectionState = std::vector; + stg::selection selection; explicit SelectionWidget(stg::strategy &strategy, int slotHeight, QWidget *parent = nullptr); - void selectAtIndex(int slotIndex); - void deselectAll(); - void selectAll(int numberOfSlots); - void fillSelected(int fromIndex, int toIndex); - - void setSlotHeight(int newSlotHeight); - - const RawSelectionState &selection() const; - - bool selectionIsContinuous() const; - bool isSlotIndexSelected(stg::strategy::time_slot_index_t slotIndex); - - void reloadStrategy(); - - bool isClicked() const; - void setIsClicked(bool isClicked); signals: void selectionChanged(); private: - using SelectionState = std::vector; - stg::strategy &strategy; - RawSelectionState rawSelectionState; - SelectionState selectionState; int slotHeight = ApplicationSettings::defaultSlotHeight; - bool _isClicked = false; - void updateUI(); - static SelectionState makeSelectionState(RawSelectionState rawState); - void drawSelectionForItem(SelectionWidget::RawSelectionState &selectionItem, QPainter &painter); + void drawSelectionForItem(const stg::grouped_selection_element &selectionItem, + QPainter &painter); void resizeEvent(QResizeEvent *event) override; void paintEvent(QPaintEvent *event) override; diff --git a/ui/sessionsmainwidget.cpp b/ui/sessionsmainwidget.cpp index 589d894..ca097ed 100644 --- a/ui/sessionsmainwidget.cpp +++ b/ui/sessionsmainwidget.cpp @@ -10,7 +10,7 @@ #include "overviewwidget.h" #include "strategysettingswidget.h" #include "currentsessionwidget.h" -#include "slotboard.h" +#include "slotboardwidget.h" SessionsMainWidget::SessionsMainWidget(stg::strategy &strategy, QWidget *parent) @@ -53,15 +53,15 @@ void SessionsMainWidget::layoutChildWidgets() { _slotBoardScrollArea->setWidgetResizable(true); _slotBoardScrollArea->setFrameShape(QFrame::NoFrame); - slotBoard = new SlotBoard(strategy); + slotBoard = new SlotBoardWidget(strategy); connect(slotBoard, - &SlotBoard::timerTick, + &SlotBoardWidget::timerTick, this, &SessionsMainWidget::updateTimerDependants); connect(slotBoard, - &SlotBoard::timeSlotsChange, + &SlotBoardWidget::timeSlotsChange, this, &SessionsMainWidget::updateOverviewWidget); @@ -91,9 +91,6 @@ void SessionsMainWidget::clearSelection() { slotBoard->clearSelection(); } -const SelectionWidget::RawSelectionState &SessionsMainWidget::selection() { - return slotBoard->selection(); -} void SessionsMainWidget::updateTimerDependants() { overviewWidget->update(); diff --git a/ui/sessionsmainwidget.h b/ui/sessionsmainwidget.h index 76879c6..be74019 100644 --- a/ui/sessionsmainwidget.h +++ b/ui/sessionsmainwidget.h @@ -18,7 +18,7 @@ class NotifierImplementation; class StrategySettingsWidget; class CurrentSessionWidget; class QScrollArea; -class SlotBoard; +class SlotBoardWidget; class OverviewWidget; class SessionsMainWidget : public QWidget, public ColorProvider { @@ -30,7 +30,6 @@ Q_OBJECT void toggleStrategySettingsOpen(); void focusOnCurrentTime(); - const SelectionWidget::RawSelectionState &selection(); void clearSelection(); void reloadStrategy(); @@ -42,7 +41,7 @@ Q_OBJECT StrategySettingsWidget *strategySettingsWidget = nullptr; CurrentSessionWidget *currentSessionWidget = nullptr; QScrollArea *_slotBoardScrollArea = nullptr; - SlotBoard *slotBoard = nullptr; + SlotBoardWidget *slotBoard = nullptr; OverviewWidget *overviewWidget = nullptr; void layoutChildWidgets(); diff --git a/ui/sessionwidget.cpp b/ui/sessionwidget.cpp index 81db7cf..f09d4ea 100644 --- a/ui/sessionwidget.cpp +++ b/ui/sessionwidget.cpp @@ -14,6 +14,7 @@ #include "strategy.h" #include "colorutils.h" #include "fontutils.h" +#include "applicationsettings.h" SessionWidget::SessionWidget(stg::session activitySession, QWidget *parent) @@ -193,7 +194,7 @@ int SessionWidget::expectedHeight() { void SessionWidget::drawLabel(QPainter &painter) const { auto font = QFont(); font.setBold(true); - font.setPixelSize(15); + font.setPixelSize(ApplicationSettings::sessionFontSize); painter.setFont(font); diff --git a/ui/slotboardcircleswidget.cpp b/ui/slotboardcircleswidget.cpp new file mode 100644 index 0000000..b6e9f7c --- /dev/null +++ b/ui/slotboardcircleswidget.cpp @@ -0,0 +1,63 @@ +// +// Created by Dmitry Khrykin on 2020-01-31. +// + +#include + +#include "slotboardcircleswidget.h" +#include "applicationsettings.h" + +SlotBoardCirclesWidget::SlotBoardCirclesWidget(const std::function &getSlotSize, + const std::function &getSlotsRect, + QWidget *parent) + : QWidget(parent), + getSlotHeight(getSlotSize), + getSlotsRect(getSlotsRect) { + setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void SlotBoardCirclesWidget::paintEvent(QPaintEvent *event) { + auto painter = QPainter(this); + + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::red); + + if (_slotBeforeResizeBoundaryIndex >= -1) { + using namespace ApplicationSettings; + + painter.setRenderHint(QPainter::Antialiasing); + + painter.setPen(QPen(highlightColor(), 2)); + painter.setBrush(baseColor()); + + auto slotsRect = getSlotsRect(); + + auto radius = 6; + auto firstSlotTopOffset = slotsRect.top() + 2; + + auto topOffset = firstSlotTopOffset + + (_slotBeforeResizeBoundaryIndex + 1) * getSlotHeight() + - 1 - radius / 2; + + auto circleLeftRect = QRect(slotsRect.left() + defaultPadding / 2, + topOffset, + radius, + radius); + auto circleRightRect = QRect(slotsRect.right() - radius - defaultPadding + defaultPadding / 2, + topOffset, + radius, + radius); + + painter.drawEllipse(circleLeftRect); + painter.drawEllipse(circleRightRect); + } +} + +void SlotBoardCirclesWidget::updateResizeBoundary(int, int slotBeforeResizeBoundaryIndex) { + this->_slotBeforeResizeBoundaryIndex = slotBeforeResizeBoundaryIndex; + update(); +} + +int SlotBoardCirclesWidget::slotBeforeResizeBoundaryIndex() const { + return _slotBeforeResizeBoundaryIndex; +} diff --git a/ui/slotboardcircleswidget.h b/ui/slotboardcircleswidget.h new file mode 100644 index 0000000..39e9efb --- /dev/null +++ b/ui/slotboardcircleswidget.h @@ -0,0 +1,34 @@ +// +// Created by Dmitry Khrykin on 2020-01-31. +// + +#ifndef STRATEGR_SLOTBOARDCIRCLESWIDGET_H +#define STRATEGR_SLOTBOARDCIRCLESWIDGET_H + +#include +#include +#include +#include + +#include "colorprovider.h" + +class SlotBoardCirclesWidget : public QWidget, + public ColorProvider { +public: + explicit SlotBoardCirclesWidget(const std::function &getSlotSize, + const std::function &getSlotsRect, + QWidget *parent); + + void updateResizeBoundary(int, int slotBeforeResizeBoundaryIndex); + int slotBeforeResizeBoundaryIndex() const; + +private: + std::function getSlotHeight; + std::function getSlotsRect; + int _slotBeforeResizeBoundaryIndex = -2; + + void paintEvent(QPaintEvent *) override; +}; + + +#endif //STRATEGR_SLOTBOARDCIRCLESWIDGET_H diff --git a/ui/slotboard.cpp b/ui/slotboardwidget.cpp similarity index 67% rename from ui/slotboard.cpp rename to ui/slotboardwidget.cpp index bf2af43..400f1f8 100644 --- a/ui/slotboard.cpp +++ b/ui/slotboardwidget.cpp @@ -1,6 +1,6 @@ #include +#include -#include #include #include #include @@ -9,13 +9,14 @@ #include #include -#include "slotboard.h" +#include "slotboardwidget.h" #include "utils.h" #include "mainwindow.h" #include "slotruler.h" #include "currenttimemarker.h" +#include "slotboardcircleswidget.h" -SlotBoard::SlotBoard(stg::strategy &strategy, QWidget *parent) +SlotBoardWidget::SlotBoardWidget(stg::strategy &strategy, QWidget *parent) : strategy(strategy), QWidget(parent) { auto *mainLayout = new QHBoxLayout(); setLayout(mainLayout); @@ -28,21 +29,23 @@ SlotBoard::SlotBoard(stg::strategy &strategy, QWidget *parent) setupCurrentTimeTimer(); } -void SlotBoard::setupCurrentTimeTimer() { - currentTimeTimer.set_callback(this, &SlotBoard::timerCallback); +void SlotBoardWidget::setupCurrentTimeTimer() { + currentTimeTimer.set_callback(this, &SlotBoardWidget::timerCallback); currentTimeTimer.start(ApplicationSettings::currentTimeTimerSecondsInterval); } -void SlotBoard::layoutChildWidgets(QHBoxLayout *mainLayout) { +void SlotBoardWidget::layoutChildWidgets(QHBoxLayout *mainLayout) { slotsWidget = new SlotsWidget(strategy); connect(slotsWidget, &SlotsWidget::sessionsChanged, this, - &SlotBoard::handleTimeSlotsChange); + &SlotBoardWidget::handleTimeSlotsChange); + slotRuler = new SlotRuler(makeLabelsForStrategy(), slotsWidget->slotHeight()); + slotsLayout = new QVBoxLayout(); slotsLayout->setSpacing(0); slotsLayout->addWidget(slotsWidget); @@ -53,25 +56,38 @@ void SlotBoard::layoutChildWidgets(QHBoxLayout *mainLayout) { mainLayout->addWidget(slotRuler); mainLayout->addLayout(slotsLayout); + circlesWidget = new SlotBoardCirclesWidget( + std::bind(&SlotsWidget::slotHeight, slotsWidget), + std::bind(&SlotsWidget::geometry, slotsWidget), + this + ); + + connect(slotsWidget, + &SlotsWidget::resizeBoundaryChanged, + circlesWidget, + &SlotBoardCirclesWidget::updateResizeBoundary); + + circlesWidget->setGeometry(geometry()); + currentTimeMarkerWidget = new CurrentTimeMarkerWidget(this); } -void SlotBoard::updateSlotsLayout() const { +void SlotBoardWidget::updateSlotsLayout() const { slotsLayout->setContentsMargins(0, slotsWidget->slotHeight() / 2, 0, 0); } -void SlotBoard::reloadStrategy() { +void SlotBoardWidget::reloadStrategy() { slotsWidget->reloadStrategy(); updateUI(); } -void SlotBoard::updateUI() { +void SlotBoardWidget::updateUI() { slotRuler->setLabels(makeLabelsForStrategy()); updateCurrentTimeMarker(); } -QVector SlotBoard::makeLabelsForStrategy() { +QVector SlotBoardWidget::makeLabelsForStrategy() { QVector labels; for (auto &timeSlot : strategy.time_slots()) { @@ -85,15 +101,15 @@ QVector SlotBoard::makeLabelsForStrategy() { return labels; } -void SlotBoard::clearSelection() { +void SlotBoardWidget::clearSelection() { slotsWidget->deselectAllSlots(); } -const SelectionWidget::RawSelectionState &SlotBoard::selection() { - return slotsWidget->selection(); -} +//const SelectionWidget::RawSelectionState &SlotBoardWidget::selection() { +// return slotsWidget->selection(); +//} -void SlotBoard::updateCurrentTimeMarker() { +void SlotBoardWidget::updateCurrentTimeMarker() { auto currentTimeMarker = stg::current_time_marker(strategy); auto rect = currentTimeMarker.rect_in_parent(slotsWidget->geometry(), @@ -110,7 +126,7 @@ void SlotBoard::updateCurrentTimeMarker() { } } -void SlotBoard::focusOnCurrentTime() { +void SlotBoardWidget::focusOnCurrentTime() { auto topOffset = stg::current_time_marker(strategy) .scroll_offset_in_parent(slotsWidget->geometry(), window()->geometry().height()); @@ -126,18 +142,18 @@ void SlotBoard::focusOnCurrentTime() { animation->start(); } -QScrollArea *SlotBoard::parentScrollArea() { +QScrollArea *SlotBoardWidget::parentScrollArea() { return qobject_cast(parentWidget()->parentWidget()); } -void SlotBoard::handleTimeSlotsChange() { +void SlotBoardWidget::handleTimeSlotsChange() { updateCurrentTimeMarker(); slotRuler->setLabels(makeLabelsForStrategy()); emit timeSlotsChange(); } -void SlotBoard::paintEvent(QPaintEvent *event) { +void SlotBoardWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setPen(Qt::NoPen); @@ -146,14 +162,16 @@ void SlotBoard::paintEvent(QPaintEvent *event) { painter.drawRect(QRect(0, 0, width(), height())); } -void SlotBoard::timerCallback() { - dispatchToMainThread([&]() { +void SlotBoardWidget::timerCallback() { + dispatchToMainThread([this]() { emit timerTick(); updateCurrentTimeMarker(); }); } -void SlotBoard::resizeEvent(QResizeEvent *event) { +void SlotBoardWidget::resizeEvent(QResizeEvent *event) { updateCurrentTimeMarker(); + + circlesWidget->setGeometry(geometry()); } diff --git a/ui/slotboard.h b/ui/slotboardwidget.h similarity index 82% rename from ui/slotboard.h rename to ui/slotboardwidget.h index 6f31d43..219938a 100644 --- a/ui/slotboard.h +++ b/ui/slotboardwidget.h @@ -14,21 +14,21 @@ #include "timer.h" class MainWindow; -class SlotBoard : +class SlotBoardCirclesWidget; + +class SlotBoardWidget : public QWidget, public ColorProvider { Q_OBJECT public: - explicit SlotBoard(stg::strategy &strategy, - QWidget *parent = nullptr); + explicit SlotBoardWidget(stg::strategy &strategy, + QWidget *parent = nullptr); void reloadStrategy(); void clearSelection(); void focusOnCurrentTime(); - const SelectionWidget::RawSelectionState &selection(); - signals: void timerTick(); void timeSlotsChange(); @@ -36,12 +36,16 @@ Q_OBJECT stg::strategy &strategy; SlotsWidget *slotsWidget = nullptr; + SlotBoardCirclesWidget *circlesWidget = nullptr; + SlotRuler *slotRuler = nullptr; QVBoxLayout *slotsLayout = nullptr; CurrentTimeMarkerWidget *currentTimeMarkerWidget = nullptr; stg::timer currentTimeTimer; + int slotBeforeResizeBoundaryIndex = -2; + void updateCurrentTimeMarker(); QVector makeLabelsForStrategy(); diff --git a/ui/slotsmousehandler.cpp b/ui/slotsmousehandler.cpp index 9f126a8..062a497 100644 --- a/ui/slotsmousehandler.cpp +++ b/ui/slotsmousehandler.cpp @@ -19,188 +19,27 @@ SlotsMouseHandler::SlotsMouseHandler(SlotsWidget *slotsWidget) : QFrame(nullptr), slotsWidget(slotsWidget) { setMouseTracking(true); + setFocusPolicy(Qt::StrongFocus); using namespace std::placeholders; handler.on_cursor_change = std::bind(&SlotsMouseHandler::updateCursor, this, _1); - handler.on_set_selected = std::bind(&SelectionWidget::setIsClicked, slotsWidget->selectionWidget, _1); -} + handler.on_select_sessions = std::bind(&SlotsMouseHandler::selectSessionsAtIndices, this, _1); + handler.on_resize_boundary_change = std::bind(&SlotsMouseHandler::updateResizeBoundary, this, _1, _2); + handler.on_open_activities = std::bind(&SlotsWidget::openActivitiesWindow, slotsWidget); -void SlotsMouseHandler::mousePressEvent(QMouseEvent *event) { - handler.mouse_press(event); -// mousePressHappened = true; -// -// auto leftButtonPressed = mouse_event->buttons() == Qt::LeftButton; -// auto rightButtonPressed = mouse_event->buttons() == Qt::RightButton; - auto ctrlPressed = event->modifiers() == Qt::CTRL; -// -// currentSlotIndex = slotIndexForEvent(mouse_event); -// clickedSessionIndex = sessionIndexForSlotIndex(currentSlotIndex); -// -// if (leftButtonPressed && ctrlPressed) { -// initDragSelectedOperation(); -// } else if (leftButtonPressed) { -// handleLeftButtonPress(mouse_event); -// } } -void SlotsMouseHandler::handleLeftButtonPress(const QMouseEvent *event) { - if (isSlotIndexSelected(currentSlotIndex)) { - operation = Operation::OpenActivities; - slotsWidget->selectionWidget->setIsClicked(true); - return; - } - - if (strategy().time_slots()[currentSlotIndex].activity == stg::strategy::no_activity) { - initDragSelectedOperation(); - return; - } - - mousePressHappened = true; - - slotsWidget->deselectAllSlots(); - - currentMouseZone = determineMouseZone(event, clickedSessionIndex); - operation = determineOperationForMouseZone(*currentMouseZone); - - previousMouseTop = event->pos().y(); - - handleSlotIndex = currentSlotIndex; - - if (operation == Operation::Drag) { - selectSessionAtSlotIndex(currentSlotIndex); - strategy().begin_dragging(sessionIndexForSlotIndex(currentSlotIndex)); - } else if (operation == Operation::StretchTop || - operation == Operation::StretchBottom) { - selectStetchingSessions(handleSlotIndex); - strategy().begin_resizing(); - } - -// updateCursor(pointer); +int SlotsMouseHandler::slotHeight() { + return slotsWidget->_slotHeight; } -void SlotsMouseHandler::initDragSelectedOperation() { - operation = Operation::DragSelect; - selectSlotAtIndex(currentSlotIndex); - handleSlotIndex = currentSlotIndex; +stg::selection &SlotsMouseHandler::selection() { + return slotsWidget->selectionWidget->selection; } -void SlotsMouseHandler::mouseMoveEvent(QMouseEvent *event) { - handler.mouse_move(event); -// currentSlotIndex = slotIndexForEvent(event); -// -// if (!mousePressHappened) { -// handleMouseHover(event); -// return; -// } -// -// checkForDirectionChange(event); -// -// auto currentMouseTop = event->pos().y(); -// auto delta = getMouseTopDelta(currentMouseTop, previousMouseTop); -// -// if (delta < slotHeight() / 2 && operation_type != Operation::Drag) { -// return; -// } -// -// previousMouseTop = currentMouseTop; -// -// handleAutoScroll(event); -// -// if (operation_type == Operation::DragSelect) { -// if (!isSlotIndexSelected(currentSlotIndex)) -// fillSelection(handleSlotIndex, currentSlotIndex); -// return; -// } -// -// if (operation_type == Operation::Drag) { -// handleDragOperation(event); -// } -// -// if (operation_type == Operation::StretchTop || -// operation_type == Operation::StretchBottom) { -// handleStretchOperation(event); -// } -} - -void SlotsMouseHandler::handleAutoScroll(const QMouseEvent *event) { - if (isPerformingAutoscroll) { - return; - } - - auto topOffsetInViewPort = mapTo(slotBoardScrollArea()->viewport(), event->pos()).y(); - - auto topAutoScrollBreakpoint = slotsWidget->slotHeight(); - auto bottomAutoScrollBreakpoint = slotBoardScrollArea()->height() - slotsWidget->slotHeight(); - - auto needsAutoScrollBottom = direction == Direction::Down && - topOffsetInViewPort > bottomAutoScrollBreakpoint && - topOffsetInViewPort <= slotBoardScrollArea()->height(); - auto needsAutoScrollTop = direction == Direction::Up && - topOffsetInViewPort < topAutoScrollBreakpoint && - topOffsetInViewPort >= 0; - - auto needsAutoScroll = !isPerformingAutoscroll && (needsAutoScrollTop || needsAutoScrollBottom); - - const auto scrollSpeed = 5; - - int scrollLength = 0; - int endValue = verticalScrollBar()->value(); - if (needsAutoScrollBottom) { - scrollLength = slotsWidget->height() - relativeTop(event); - endValue = verticalScrollBar()->maximum(); - } else if (needsAutoScrollTop) { - scrollLength = relativeTop(event); - endValue = verticalScrollBar()->minimum(); - } - - if (needsAutoScroll) { - if (!autoscrollAnimation) - autoscrollAnimation = new QPropertyAnimation(verticalScrollBar(), - "value", - this); - autoscrollAnimation->disconnect(); - - connect(autoscrollAnimation, - &QAbstractAnimation::finished, - [this]() { - isPerformingAutoscroll = false; - }); - - auto fakeEvent = *event; - connect(autoscrollAnimation, - &QVariantAnimation::valueChanged, - [this, - &fakeEvent, - topOffsetInViewPort](const QVariant &value) { - - auto directionMargin = 1; - if (direction == Direction::Up) { - directionMargin *= -1; - } - - auto topInSlotBoard = value.toInt() + topOffsetInViewPort + directionMargin; - fakeEvent.setLocalPos(QPoint(fakeEvent.pos().x(), topInSlotBoard)); - - mouseMoveEvent(&fakeEvent); - }); - - if (scrollLength > 0) { - autoscrollAnimation->setDuration(scrollSpeed * scrollLength); - autoscrollAnimation->setStartValue(verticalScrollBar()->value()); - autoscrollAnimation->setEndValue(endValue); - - isPerformingAutoscroll = true; - autoscrollAnimation->start(); - } else { - resetAutoscrollAnimation(); - } - } -} - -QScrollBar * -SlotsMouseHandler::verticalScrollBar() const { +QScrollBar *SlotsMouseHandler::verticalScrollBar() const { return slotBoardScrollArea()->verticalScrollBar(); } @@ -211,252 +50,130 @@ QScrollArea *SlotsMouseHandler::slotBoardScrollArea() const { ->slotBoardScrollArea(); } -bool SlotsMouseHandler::checkForDirectionChange(const QMouseEvent *event) { - auto previousDirection = direction; - auto newDirection = determineDirection(event); - - if (newDirection.has_value()) { - direction = newDirection; - } - - if (previousDirection.has_value() && - direction != previousDirection) { - - if (operation != Operation::Drag) - handleResizeDirectionChange(previousDirection); - - if (isPerformingAutoscroll) { - auto currentMouseTop = event->pos().y(); - auto delta = getMouseTopDelta(currentMouseTop, previousMouseTop); - if (delta > 10) { - resetAutoscrollAnimation(); - handleAutoScroll(event); - } - } - - return true; - } - - return false; +void SlotsMouseHandler::mousePressEvent(QMouseEvent *event) { + handler.mouse_press(event); } -int SlotsMouseHandler::getMouseTopDelta(int currentMouseTop, int previousMouseTop) { - auto totalHeight = static_cast(previousMouseTop - firstSlotTopOffset()); - auto closestSlotBoundary = round(totalHeight / slotHeight()) * slotHeight(); - - return std::abs(static_cast(currentMouseTop - closestSlotBoundary));; +void SlotsMouseHandler::mouseMoveEvent(QMouseEvent *event) { + handler.mouse_move(event); } - -std::optional -SlotsMouseHandler::determineDirection(const QMouseEvent *event) { - auto currentMouseTop = event->pos().y(); - - std::optional direction; - if (currentMouseTop < previousMouseTop) { - direction = Direction::Up; - } else if (currentMouseTop > previousMouseTop) { - direction = Direction::Down; - } else { - direction = std::nullopt; - } - - return direction; +void SlotsMouseHandler::mouseReleaseEvent(QMouseEvent *event) { + handler.mouse_release(event); } +void SlotsMouseHandler::leaveEvent(QEvent *) { -void SlotsMouseHandler::handleResizeDirectionChange(const std::optional &previousDirection) { - if (!resizeBoundarySlotIndex.has_value()) { - return; - } - - if (operation == Operation::StretchTop && - previousDirection == Direction::Down) { - if (handleSlotIndex != *resizeBoundarySlotIndex + 1) - handleSlotIndex = *resizeBoundarySlotIndex + 1; - } - - if (operation == Operation::StretchBottom && - previousDirection == Direction::Up) { - if (handleSlotIndex != *resizeBoundarySlotIndex) - handleSlotIndex = *resizeBoundarySlotIndex; - } } - -void SlotsMouseHandler::handleStretchOperation(QMouseEvent *event) { - if (!resizeBoundarySlotIndex.has_value()) { - return; - } - - if (operation == Operation::StretchTop && - direction == Direction::Down) { - if (handleSlotIndex != *resizeBoundarySlotIndex) - handleSlotIndex = *resizeBoundarySlotIndex; - } - - if (operation == Operation::StretchBottom && - direction == Direction::Up) { - if (handleSlotIndex != *resizeBoundarySlotIndex + 1) - handleSlotIndex = *resizeBoundarySlotIndex + 1; - } - - strategy().fill_time_slots(handleSlotIndex, currentSlotIndex); - - handleSlotIndex = currentSlotIndex; - - selectStetchingSessions(handleSlotIndex); +void SlotsMouseHandler::keyPressEvent(QKeyEvent *event) { + QWidget::keyPressEvent(event); + handler.key_down(event); } - -void SlotsMouseHandler::selectStetchingSessions(int sourceIndex) { - auto firstSelectionIndex = static_cast( - strategy().sessions() - .session_index_for_time_slot_index(sourceIndex) - ); - - if (operation == Operation::StretchTop && - direction == Direction::Down) { - firstSelectionIndex++; - } - - if (operation == Operation::StretchBottom && - direction == Direction::Up) { - firstSelectionIndex--; - } - - int secondSelectionIndex = firstSelectionIndex + 1; - - if (operation == Operation::StretchTop) { - secondSelectionIndex = firstSelectionIndex - 1; - } - - if (firstSelectionIndex < 0 || - firstSelectionIndex >= strategy().sessions().size() || - secondSelectionIndex < 0 || - secondSelectionIndex >= strategy().sessions().size() || - (strategy().sessions()[firstSelectionIndex].activity == stg::strategy::no_activity && - strategy().sessions()[secondSelectionIndex].activity == stg::strategy::no_activity)) { - deselectAllSessions(); - resizeBoundarySlotIndex = std::nullopt; - update(); - reset(); - return; - } - - auto selectBorderIndex = std::min(firstSelectionIndex, secondSelectionIndex); - - for (int i = 0; i < strategy().sessions().size(); i++) { - sessionWidgetAtIndex(i)->setSelectBorder(i == selectBorderIndex); - } - - selectSessionsAtIndices({firstSelectionIndex, secondSelectionIndex}); - - auto borderSlot = strategy().sessions()[selectBorderIndex].time_slots.back(); - resizeBoundarySlotIndex = strategy().time_slots().index_of(borderSlot); - - update(); +void SlotsMouseHandler::keyReleaseEvent(QKeyEvent *event) { + QWidget::keyReleaseEvent(event); + handler.key_up(event); } -void SlotsMouseHandler::handleMouseHover(const QMouseEvent *event) { - clickedSessionIndex = sessionIndexForSlotIndex(currentSlotIndex); - - if (clickedSessionIndex < 0) { - return; - } - - auto mouseZone = determineMouseZone(event, clickedSessionIndex); - - if (mouseZone != currentMouseZone) { - currentMouseZone = mouseZone; -// updateCursor(pointer); - } -} - -void SlotsMouseHandler::handleDragOperation(QMouseEvent *event) { - auto distance = currentSlotIndex - handleSlotIndex; - - strategy().drag_activity_session(clickedSessionIndex, distance); - - handleSlotIndex = currentSlotIndex; - clickedSessionIndex = sessionIndexForSlotIndex(currentSlotIndex); - - selectSessionAtSlotIndex(currentSlotIndex); -} - - -void SlotsMouseHandler::mouseReleaseEvent(QMouseEvent *event) { - handler.mouse_release(event); -// if (operation_type == Operation::OpenActivities) { -// slotsWidget->openActivitiesWindow(); +void SlotsMouseHandler::handleAutoScroll(const QMouseEvent *event) { +// if (isPerformingAutoscroll) { +// return; // } // -// if (mousePressHappened) { -// teardown(); +// auto topOffsetInViewPort = mapTo(slotBoardScrollArea()->viewport(), event->pos()).y(); +// +// auto topAutoScrollBreakpoint = slotsWidget->slotHeight(); +// auto bottomAutoScrollBreakpoint = slotBoardScrollArea()->height() - slotsWidget->slotHeight(); +// +// auto needsAutoScrollBottom = direction == Direction::Down && +// topOffsetInViewPort > bottomAutoScrollBreakpoint && +// topOffsetInViewPort <= slotBoardScrollArea()->height(); +// auto needsAutoScrollTop = direction == Direction::Up && +// topOffsetInViewPort < topAutoScrollBreakpoint && +// topOffsetInViewPort >= 0; +// +// auto needsAutoScroll = !isPerformingAutoscroll && (needsAutoScrollTop || needsAutoScrollBottom); +// +// const auto scrollSpeed = 5; +// +// int scrollLength = 0; +// int endValue = verticalScrollBar()->value(); +// +// if (needsAutoScrollBottom) { +// scrollLength = slotsWidget->height() - relativeTop(event); +// endValue = verticalScrollBar()->maximum(); +// } else if (needsAutoScrollTop) { +// scrollLength = relativeTop(event); +// endValue = verticalScrollBar()->minimum(); +// } +// +// if (needsAutoScroll) { +// if (!autoscrollAnimation) +// autoscrollAnimation = new QPropertyAnimation(verticalScrollBar(), +// "value", +// this); +// autoscrollAnimation->disconnect(); +// +// connect(autoscrollAnimation, +// &QAbstractAnimation::finished, +// [this]() { +// isPerformingAutoscroll = false; +// }); +// +// auto fakeEvent = *event; +// connect(autoscrollAnimation, +// &QVariantAnimation::valueChanged, +// [this, +// &fakeEvent, +// topOffsetInViewPort](const QVariant &value) { +// +// auto directionMargin = 1; +// if (direction == Direction::Up) { +// directionMargin *= -1; +// } +// +// auto topInSlotBoard = value.toInt() + topOffsetInViewPort + directionMargin; +// fakeEvent.setLocalPos(QPoint(fakeEvent.pos().x(), topInSlotBoard)); +// +// mouseMoveEvent(&fakeEvent); +// }); +// +// if (scrollLength > 0) { +// autoscrollAnimation->setDuration(scrollSpeed * scrollLength); +// autoscrollAnimation->setStartValue(verticalScrollBar()->value()); +// autoscrollAnimation->setEndValue(endValue); +// +// isPerformingAutoscroll = true; +// autoscrollAnimation->start(); +// } else { +// resetAutoscrollAnimation(); +// } // } } - -int SlotsMouseHandler::sessionIndexForSlotIndex(int slotIndex) { - return strategy().sessions() - .session_index_for_time_slot_index(slotIndex); -} - -std::optional -SlotsMouseHandler::determineOperationForMouseZone(MouseZone mouseZone) { - auto currentSlotIsEmpty - = strategy().time_slots()[currentSlotIndex] - .activity == stg::strategy::no_activity; - - switch (mouseZone) { - case MouseZone::DragZone: - if (currentSlotIsEmpty) { - return std::nullopt; - } - - return Operation::Drag; - case MouseZone::TopStretchZone: - return Operation::StretchTop; - case MouseZone::BottomStretchZone: - return Operation::StretchBottom; +void SlotsMouseHandler::updateResizeBoundary(int sessionBeforeBoundaryIndex, int slotBeforeBoundaryIndex) { + for (int i = 0; i < strategy().sessions().size(); i++) { + sessionWidgetAtIndex(i)->setSelectBorder(i == sessionBeforeBoundaryIndex); } -} - - -SlotsMouseHandler::MouseZone -SlotsMouseHandler::determineMouseZone(const QMouseEvent *event, int sessionIndex) { - auto *currentSessionWidget = sessionWidgetAtIndex(sessionIndex); - - auto topStretchZone = StretchZonePosition{currentSessionWidget->geometry().top()}; - auto bottomStretchZone = StretchZonePosition{currentSessionWidget->geometry().top() - + currentSessionWidget->height() - - stretchZoneHeight}; - if (event->pos().y() >= topStretchZone.top() && - event->pos().y() <= topStretchZone.bottom()) { - return MouseZone::TopStretchZone; - } else if (event->pos().y() >= bottomStretchZone.top() && - event->pos().y() <= bottomStretchZone.bottom()) { - return MouseZone::BottomStretchZone; - } else { - return MouseZone::DragZone; - } + emit resizeBoundaryChanged(sessionBeforeBoundaryIndex, slotBeforeBoundaryIndex); } -void SlotsMouseHandler::updateCursor(stg::mousehandler::cursor new_cursor) { +void SlotsMouseHandler::updateCursor(stg::mouse_handler::cursor new_cursor) { using namespace stg; switch (new_cursor) { - case mousehandler::cursor::pointer: + case mouse_handler::cursor::pointer: unsetCursor(); break; - case mousehandler::cursor::resize: + case mouse_handler::cursor::resize: setCursor(resizeVerticalCursor()); break; - case mousehandler::cursor::closed_hand: + case mouse_handler::cursor::closed_hand: setCursor(closedHandCursor()); break; - case mousehandler::cursor::open_hand: + case mouse_handler::cursor::open_hand: setCursor(openHandCursor()); break; } @@ -464,50 +181,27 @@ void SlotsMouseHandler::updateCursor(stg::mousehandler::cursor new_cursor) { void SlotsMouseHandler::contextMenuEvent(QContextMenuEvent *event) { - auto currentSlotIndex = slotIndexForEvent(event); - auto ¤tSlot = strategy().time_slots()[currentSlotIndex]; - - QMenu menu(slotsWidget); - menu.addAction(slotsWidget->setActivityAction); - slotsWidget->setActivityAction->setEnabled(hasSelection()); - - menu.addSeparator(); - - if (currentSlot.activity && hasSelection()) { - menu.addAction(slotsWidget->deleteActivityAction); - } - - menu.addAction(slotsWidget->shiftSlotsBelowAction); - menu.addSeparator(); - - menu.addAction(slotsWidget->clearSelectionAction); - slotsWidget->clearSelectionAction->setEnabled(hasSelection()); - - - menu.exec(event->globalPos()); -} - -bool SlotsMouseHandler::hasSelection() { - return !slotsWidget->selectionWidget->selection().empty(); -} - -void SlotsMouseHandler::selectSlotAtIndex(int slotIndex) { - if (slotIndex < 0 || - slotIndex >= strategy().number_of_time_slots()) { - return; - } - - slotsWidget->selectionWidget->selectAtIndex(slotIndex); - slotsWidget->clearSelectionAction->setEnabled(true); -} - -void SlotsMouseHandler::selectSessionAtSlotIndex(int slotIndex) { - auto sessionIndex = strategy().sessions() - .session_index_for_time_slot_index(slotIndex); - - if (sessionIndex) { - selectSessionsAtIndices({static_cast(sessionIndex)}); - } +// auto currentSlotIndex = slotIndexForEvent(event); +// auto ¤tSlot = strategy().time_slots()[currentSlotIndex]; +// +// QMenu menu(slotsWidget); +// menu.addAction(slotsWidget->setActivityAction); +// slotsWidget->setActivityAction->setEnabled(handler.selection.empty()); +// +// menu.addSeparator(); +// +// if (currentSlot.activity && hasSelection()) { +// menu.addAction(slotsWidget->deleteActivityAction); +// } +// +// menu.addAction(slotsWidget->shiftSlotsBelowAction); +// menu.addSeparator(); +// +// menu.addAction(slotsWidget->clearSelectionAction); +// slotsWidget->clearSelectionAction->setEnabled(hasSelection()); +// +// +// menu.exec(event->globalPos()); } void SlotsMouseHandler::selectSessionsAtIndices(const std::vector &sessionIndices) { @@ -519,13 +213,6 @@ void SlotsMouseHandler::selectSessionsAtIndices(const std::vector &sessionI } } - -void SlotsMouseHandler::deselectAllSessions() { - for (int i = 0; i < slotsWidget->slotsLayout->count(); i++) { - setSelectedForSessionIndex(i, false); - } -} - void SlotsMouseHandler::setSelectedForSessionIndex(int sessionIndex, bool isSelected) { auto *sessionWidget = sessionWidgetAtIndex(sessionIndex); @@ -535,9 +222,6 @@ void SlotsMouseHandler::setSelectedForSessionIndex(int sessionIndex, } } -int SlotsMouseHandler::slotHeight() { - return slotsWidget->_slotHeight; -} SessionWidget *SlotsMouseHandler::sessionWidgetAtIndex(int sessionIndex) { @@ -549,49 +233,10 @@ SessionWidget return qobject_cast(sessionWidget); } -int SlotsMouseHandler::firstSlotTopOffset() { - auto firstItem = slotsWidget->slotsLayout->itemAt(0); - if (!firstItem || !firstItem->widget()) { - return 0; - } - - return firstItem->geometry().top(); -} - stg::strategy &SlotsMouseHandler::strategy() { return slotsWidget->strategy; } -void SlotsMouseHandler::reset() { - if (operation == Operation::Drag) { - strategy().end_dragging(); - } - - if (operation == Operation::StretchBottom || - operation == Operation::StretchTop) { - strategy().end_resizing(); - } - - mousePressHappened = false; - currentMouseZone = std::nullopt; - operation = std::nullopt; - direction = std::nullopt; - resizeBoundarySlotIndex = std::nullopt; - - currentSlotIndex = -1; - clickedSessionIndex = -1; - handleSlotIndex = -1; - - previousMouseTop = 0; - - resetAutoscrollAnimation(); - - unsetCursor(); - deselectAllSessions(); - - update(); -} - void SlotsMouseHandler::resetAutoscrollAnimation() { if (isPerformingAutoscroll) { isPerformingAutoscroll = false; @@ -601,65 +246,41 @@ void SlotsMouseHandler::resetAutoscrollAnimation() { } } -SessionWidget *SlotsMouseHandler::sessionWidgetAtSlotIndex(int slotIndex) { - auto sessionIndex = strategy().sessions() - .session_index_for_time_slot_index(slotIndex); - - if (!sessionIndex) { - return nullptr; - } - - - return sessionWidgetAtIndex(sessionIndex); -} +//SessionWidget *SlotsMouseHandler::sessionWidgetAtSlotIndex(int slotIndex) { +// auto sessionIndex = strategy().sessions() +// .session_index_for_time_slot_index(slotIndex); +// +// if (!sessionIndex) { +// return nullptr; +// } +// +// +// return sessionWidgetAtIndex(sessionIndex); +//} void SlotsMouseHandler::paintEvent(QPaintEvent *event) { - using namespace ApplicationSettings; - auto painter = QPainter(this); - - if (resizeBoundarySlotIndex.has_value()) { - painter.setRenderHint(QPainter::Antialiasing); - - painter.setPen(QPen(highlightColor(), 2)); - painter.setBrush(baseColor()); - - auto radius = 6; - auto topOffset = firstSlotTopOffset() - + (*resizeBoundarySlotIndex + 1) * slotHeight() - - 1 - radius / 2; - auto circleLeftRect = QRect(defaultPadding / 2, topOffset, radius, radius); - auto circleRightRect = QRect(width() - radius - defaultPadding + defaultPadding / 2, - topOffset, - radius, - radius); - - painter.drawEllipse(circleLeftRect); - painter.drawEllipse(circleRightRect); - } -} - -bool SlotsMouseHandler::isSlotIndexSelected(int slotIndex) { - return slotsWidget->selectionWidget->isSlotIndexSelected(slotIndex); -} - -void SlotsMouseHandler::fillSelection(int fromIndex, int toIndex) { - slotsWidget->selectionWidget->fillSelected(fromIndex, toIndex); -} - -void SlotsMouseHandler::leaveEvent(QEvent *) { - +// using namespace ApplicationSettings; +// auto painter = QPainter(this); +// +// if (slotBeforeResizeBoundaryIndex >= -1) { +// painter.setRenderHint(QPainter::Antialiasing); +// +// painter.setPen(QPen(highlightColor(), 2)); +// painter.setBrush(baseColor()); +// +// auto radius = 6; +// auto topOffset = firstSlotTopOffset() +// + (slotBeforeResizeBoundaryIndex + 1) * slotHeight() +// - 1 - radius / 2; +// auto circleLeftRect = QRect(defaultPadding / 2, topOffset, radius, radius); +// auto circleRightRect = QRect(width() - radius - defaultPadding + defaultPadding / 2, +// topOffset, +// radius, +// radius); +// +// painter.drawEllipse(circleLeftRect); +// painter.drawEllipse(circleRightRect); +// } } -std::ostream &operator<<(std::ostream &os, SlotsMouseHandler::Direction direction) { - switch (direction) { - case SlotsMouseHandler::Direction::Up: - os << "\u2191"; - break; - case SlotsMouseHandler::Direction::Down: - os << "\u2193"; - break; - } - - return os; -} diff --git a/ui/slotsmousehandler.h b/ui/slotsmousehandler.h index ffb93c7..c84450b 100644 --- a/ui/slotsmousehandler.h +++ b/ui/slotsmousehandler.h @@ -11,7 +11,7 @@ #include "cursorprovider.h" #include "colorprovider.h" #include "sessionsmainwidget.h" -#include "mousehandler.h" +#include "mouse_handler.h" #include "slotswidget.h" class SessionWidget; @@ -21,141 +21,52 @@ class SlotsMouseHandler : public QFrame, public CursorProvider, public ColorProvider { +Q_OBJECT public: explicit SlotsMouseHandler(SlotsWidget *slotsWidget); - - void reset(); +signals: + void resizeBoundaryChanged(int sessionBeforeBoundaryIndex, + int slotBeforeBoundaryIndex); private: - static const auto stretchZoneHeight = 3; - - struct StretchZonePosition; - - enum class Operation { - Drag, - StretchTop, - StretchBottom, - DragSelect, - OpenActivities - }; - - enum class MouseZone { - DragZone, - TopStretchZone, - BottomStretchZone, - }; - - enum class Direction { - Up, - Down - }; - SlotsWidget *slotsWidget; - stg::mousehandler handler{slotsWidget->strategy, - [this]() { - return slotsWidget->_slotHeight; - }, - [this]() { - return stg::rect(slotsWidget->slotsLayout->contentsRect()); - }}; - - std::optional operation = std::nullopt; - std::optional direction = std::nullopt; - - std::optional currentMouseZone = std::nullopt; - - int currentSlotIndex = -1; - int clickedSessionIndex = -1; - - int previousMouseTop = 0; - int handleSlotIndex = -1; + stg::mouse_handler handler{strategy(), + selection(), + [this] { + return slotHeight(); + }, + [this] { + return stg::rect(slotsWidget->slotsLayout->contentsRect()); + }}; - std::optional resizeBoundarySlotIndex = std::nullopt; + stg::strategy &strategy(); + stg::selection &selection(); + int slotHeight(); - bool mousePressHappened = false; bool isPerformingAutoscroll = false; - QPropertyAnimation *autoscrollAnimation = nullptr; SessionWidget *sessionWidgetAtIndex(int sessionIndex); - SessionWidget *sessionWidgetAtSlotIndex(int timeSlotIndex); - - stg::strategy &strategy(); - int slotHeight(); - - int firstSlotTopOffset(); - - template - int relativeTop(Event *event) { - return event->pos().y() - firstSlotTopOffset(); - } - - template - int slotIndexForEvent(Event *event) { - return relativeTop(event) / slotHeight(); - } - bool hasSelection(); - void selectSlotAtIndex(int slotIndex); - void fillSelection(int fromIndex, int toIndex); - - void selectSessionAtSlotIndex(int slotIndex); void selectSessionsAtIndices(const std::vector &sessionIndices); - void selectStetchingSessions(int sourceIndex); + void updateResizeBoundary(int sessionBeforeBoundaryIndex, + int slotBeforeBoundaryIndex); void setSelectedForSessionIndex(int sessionIndex, bool isSelected); - void deselectAllSessions(); - - void updateCursor(stg::mousehandler::cursor new_cursor); - SlotsMouseHandler::MouseZone determineMouseZone(const QMouseEvent *event, int slotIndex); - - std::optional determineOperationForMouseZone(MouseZone mouseZone); - std::optional determineDirection(const QMouseEvent *event); - - int sessionIndexForSlotIndex(int slotIndex); - - void handleDragOperation(QMouseEvent *event); - void handleMouseHover(const QMouseEvent *event); - void handleStretchOperation(QMouseEvent *event); - void handleLeftButtonPress(const QMouseEvent *event); - void handleResizeDirectionChange(const std::optional &previousDirection); - - bool checkForDirectionChange(const QMouseEvent *event); - - int getMouseTopDelta(int currentMouseTop, int previousMouseTop); - - bool isSlotIndexSelected(int slotIndex); + void updateCursor(stg::mouse_handler::cursor new_cursor); void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void leaveEvent(QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; - void initDragSelectedOperation(); QScrollArea *slotBoardScrollArea() const; QScrollBar *verticalScrollBar() const; void handleAutoScroll(const QMouseEvent *event); void resetAutoscrollAnimation(); - - friend std::ostream &operator<<(std::ostream &os, Direction direction); }; - - -struct SlotsMouseHandler::StretchZonePosition { - explicit StretchZonePosition(int top = 0) : _top(top) {} - - int top() { - return _top; - } - - int bottom() { - return top() + stretchZoneHeight; - } - -private: - int _top = 0; -}; - - #endif //STRATEGR_SLOTSMOUSEHANDLER_H diff --git a/ui/slotswidget.cpp b/ui/slotswidget.cpp index 5a10992..0b8659c 100644 --- a/ui/slotswidget.cpp +++ b/ui/slotswidget.cpp @@ -45,6 +45,10 @@ void SlotsWidget::layoutChildWidgets() { &SlotsWidget::onSelectionChange); mouseHandler = new SlotsMouseHandler(this); + connect(mouseHandler, + &SlotsMouseHandler::resizeBoundaryChanged, + this, + &SlotsWidget::updateResizeBoundary); layout()->addWidget(slotsWidget); layout()->addWidget(selectionWidget); @@ -53,7 +57,6 @@ void SlotsWidget::layoutChildWidgets() { updateContentsMargins(); } - void SlotsWidget::setupActions() { setActivityAction = new QAction(tr("Set Activity"), this); setActivityAction->setShortcut(QKeySequence(Qt::Key_Return)); @@ -105,40 +108,35 @@ void SlotsWidget::setupActions() { int SlotsWidget::slotHeight() const { return _slotHeight; } void SlotsWidget::openActivitiesWindow() { - if (!selectionWidget->selection().empty()) { - mainScene()->showActivities(); - } + mainScene()->showActivities(); } void SlotsWidget::deleteActivityInSelection() { - strategy.make_empty_at(selectionWidget->selection()); - selectionWidget->deselectAll(); + strategy.make_empty_at(selectionWidget->selection); + selectionWidget->selection.deselect_all(); } void SlotsWidget::deselectAllSlots() { - selectionWidget->deselectAll(); + selectionWidget->selection.deselect_all(); } void SlotsWidget::selectAllSlots() { - selectionWidget->selectAll(strategy.number_of_time_slots()); + selectionWidget->selection.select_all(); } void SlotsWidget::reloadStrategy() { strategy.sessions() .add_on_change_callback(this, &SlotsWidget::updateUI); - selectionWidget->reloadStrategy(); - updateList(); - mouseHandler->reset(); } MainScene *SlotsWidget::mainScene() { return findParentWidget(this); } -const SelectionWidget::RawSelectionState &SlotsWidget::selection() { - return selectionWidget->selection(); +const stg::selection &SlotsWidget::selection() { + return selectionWidget->selection; } int SlotsWidget::numberOfItems() { @@ -170,43 +168,50 @@ void SlotsWidget::updateUI() { } void SlotsWidget::onSelectionChange() { - auto isEnabled = selectionWidget->selectionIsContinuous() + auto isEnabled = selectionWidget->selection.is_continuous() && !onlyEmptySlotsSelected(); shiftSlotsBelowAction->setEnabled(isEnabled); } void SlotsWidget::shiftAllSlotsBelow() { - if (!selectionWidget->selectionIsContinuous()) { + if (!selectionWidget->selection.is_continuous()) { return; } - auto bottomTimeSlotIndex = selectionWidget->selection().front(); + auto bottomTimeSlotIndex = selectionWidget->selection.front(); strategy.shift_below_time_slot(bottomTimeSlotIndex, - selectionWidget->selection().size()); + selectionWidget->selection.size()); - selectionWidget->deselectAll(); + selectionWidget->selection.deselect_all(); } bool SlotsWidget::onlyEmptySlotsSelected() const { - for (auto slotIndex : selectionWidget->selection()) { - if (strategy.time_slots()[slotIndex].activity - != stg::strategy::no_activity) { - return false; - } - } +// for (auto slotIndex : selectionWidget->selection()) { +// if (strategy.time_slots()[slotIndex].activity +// != stg::strategy::no_activity) { +// return false; +// } +// } return true; } void SlotsWidget::paintEvent(QPaintEvent *event) { + using namespace ApplicationSettings; + QStyleOption opt; opt.init(this); QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); painter.setPen(Qt::NoPen); - painter.setBrush(SessionWidget::borderColor()); + + auto borderColor = slotBeforeBoundaryIndex == -1 + ? highlightColor() + : ColorProvider::borderColor(); + + painter.setBrush(borderColor); auto thickness = strategy.begin_time() % 60 == 0 ? 2 : 1; auto borderRect = QRect(8, 0, width() - 8 * 2, thickness); @@ -220,6 +225,14 @@ void SlotsWidget::updateContentsMargins() { selectionWidget->setContentsMargins(8, thickness, 8, 0); } +void SlotsWidget::updateResizeBoundary(int sessionBeforeBoundaryIndex, + int slotBeforeBoundaryIndex) { + this->slotBeforeBoundaryIndex = slotBeforeBoundaryIndex; + update(); + + emit resizeBoundaryChanged(sessionBeforeBoundaryIndex, slotBeforeBoundaryIndex); +} + diff --git a/ui/slotswidget.h b/ui/slotswidget.h index c8730fd..ff1c491 100644 --- a/ui/slotswidget.h +++ b/ui/slotswidget.h @@ -14,11 +14,14 @@ #include "applicationsettings.h" #include "selectionwidget.h" #include "reactivelist.hpp" +#include "selection.h" +#include "colorprovider.h" class SlotsMouseHandler; class MainScene; class SlotsWidget : public QWidget, - public ReactiveList { + public ReactiveList, + public ColorProvider { Q_OBJECT public: explicit SlotsWidget(stg::strategy &strategy, @@ -29,9 +32,10 @@ Q_OBJECT void deselectAllSlots(); - const SelectionWidget::RawSelectionState &selection(); + const stg::selection &selection(); signals: void sessionsChanged(); + void resizeBoundaryChanged(int, int); private: friend SlotsMouseHandler; stg::strategy &strategy; @@ -42,12 +46,12 @@ Q_OBJECT SlotsMouseHandler *mouseHandler = nullptr; int _slotHeight = ApplicationSettings::defaultSlotHeight; + int slotBeforeBoundaryIndex = -2; QAction *setActivityAction = nullptr; QAction *deleteActivityAction = nullptr; QAction *clearSelectionAction = nullptr; QAction *shiftSlotsBelowAction = nullptr; - QAction *selectAllAction = nullptr; void openActivitiesWindow(); @@ -61,6 +65,7 @@ Q_OBJECT void updateUI(); void updateContentsMargins(); + void updateResizeBoundary(int sessionBeforeBoundaryIndex, int slotBeforeBoundaryIndex); void onSelectionChange(); diff --git a/utility/applicationsettings.h b/utility/applicationsettings.h index c58959a..acb2c6f 100644 --- a/utility/applicationsettings.h +++ b/utility/applicationsettings.h @@ -14,6 +14,8 @@ namespace ApplicationSettings { const auto defaultActivityItemHeight = 40; const auto defaultPadding = 8; + const auto sessionFontSize = 14; + const auto currentTimeTimerSecondsInterval = 1; const auto notifierTimerTimeInterval = 15 * 1000; const auto currentSessionShowDelay = 500; diff --git a/utility/utils.h b/utility/utils.h index 5b108b4..2bd694f 100644 --- a/utility/utils.h +++ b/utility/utils.h @@ -29,9 +29,9 @@ template T *findParentWidget(QWidget *childWidget) { auto *widget = childWidget->parentWidget(); while (widget) { - auto *mainScene = qobject_cast(widget); - if (mainScene) { - return mainScene; + auto *searchedWideget = qobject_cast(widget); + if (searchedWideget) { + return searchedWideget; } else { widget = widget->parentWidget(); }