diff --git a/libraries/ZWidget/include/zwidget/core/rect.h b/libraries/ZWidget/include/zwidget/core/rect.h index f5ab17caf8..ad2a4e09ca 100644 --- a/libraries/ZWidget/include/zwidget/core/rect.h +++ b/libraries/ZWidget/include/zwidget/core/rect.h @@ -46,6 +46,15 @@ public: static Rect xywh(double x, double y, double width, double height) { return Rect(x, y, width, height); } static Rect ltrb(double left, double top, double right, double bottom) { return Rect(left, top, right - left, bottom - top); } + static Rect shrink(Rect box, double left, double top, double right, double bottom) + { + box.x += left; + box.y += top; + box.width = std::max(box.width - left - right, 0.0); + box.height = std::max(box.height - bottom - top, 0.0); + return box; + } + bool contains(const Point& p) const { return (p.x >= x && p.x < x + width) && (p.y >= y && p.y < y + height); } double x = 0; diff --git a/libraries/ZWidget/include/zwidget/widgets/listview/listview.h b/libraries/ZWidget/include/zwidget/widgets/listview/listview.h index 0d8f632cd9..ad30cd17c2 100644 --- a/libraries/ZWidget/include/zwidget/widgets/listview/listview.h +++ b/libraries/ZWidget/include/zwidget/widgets/listview/listview.h @@ -5,6 +5,8 @@ #include #include +class Scrollbar; + class ListView : public Widget { public: @@ -12,6 +14,7 @@ public: void AddItem(const std::string& text); int GetSelectedItem() const { return selectedItem; } + void ScrollToItem(int index); void Activate(); @@ -22,7 +25,12 @@ protected: void OnPaintFrame(Canvas* canvas) override; void OnMouseDown(const Point& pos, int key) override; void OnMouseDoubleclick(const Point& pos, int key) override; + void OnMouseWheel(const Point& pos, EInputKey key) override; void OnKeyDown(EInputKey key) override; + void OnGeometryChanged() override; + void OnScrollbarScroll(); + + Scrollbar* scrollbar = nullptr; std::vector items; int selectedItem = 0; diff --git a/libraries/ZWidget/include/zwidget/widgets/scrollbar/scrollbar.h b/libraries/ZWidget/include/zwidget/widgets/scrollbar/scrollbar.h index f58261887b..38afda3832 100644 --- a/libraries/ZWidget/include/zwidget/widgets/scrollbar/scrollbar.h +++ b/libraries/ZWidget/include/zwidget/widgets/scrollbar/scrollbar.h @@ -13,24 +13,27 @@ public: bool IsVertical() const; bool IsHorizontal() const; - int GetMin() const; - int GetMax() const; - int GetLineStep() const; - int GetPageStep() const; - int GetPosition() const; + double GetMin() const; + double GetMax() const; + double GetLineStep() const; + double GetPageStep() const; + double GetPosition() const; void SetVertical(); void SetHorizontal(); - void SetMin(int scroll_min); - void SetMax(int scroll_max); - void SetLineStep(int step); - void SetPageStep(int step); + void SetMin(double scroll_min); + void SetMax(double scroll_max); + void SetLineStep(double step); + void SetPageStep(double step); - void SetRanges(int scroll_min, int scroll_max, int line_step, int page_step); - void SetRanges(int view_size, int total_size); + void SetRanges(double scroll_min, double scroll_max, double line_step, double page_step); + void SetRanges(double view_size, double total_size); - void SetPosition(int pos); + void SetPosition(double pos); + + double GetPreferredWidth() const { return 16.0; } + double GetPreferredHeight() const { return 16.0; } std::function FuncScroll; std::function FuncScrollMin; @@ -54,18 +57,20 @@ protected: private: bool UpdatePartPositions(); - int CalculateThumbSize(int track_size); - int CalculateThumbPosition(int thumb_size, int track_size); - Rect CreateRect(int start, int end); + double CalculateThumbSize(double track_size); + double CalculateThumbPosition(double thumb_size, double track_size); + Rect CreateRect(double start, double end); void InvokeScrollEvent(std::function* event_ptr); void OnTimerExpired(); - bool vertical = false; - int scroll_min = 0; - int scroll_max = 1; - int line_step = 1; - int page_step = 10; - int position = 0; + bool vertical = true; + double scroll_min = 0.0; + double scroll_max = 1.0; + double line_step = 1.0; + double page_step = 10.0; + double position = 0.0; + + bool showbuttons = false; enum MouseDownMode { @@ -77,12 +82,12 @@ private: mouse_down_thumb_drag } mouse_down_mode = mouse_down_none; - int thumb_start_position = 0; + double thumb_start_position = 0.0; Point mouse_drag_start_pos; - int thumb_start_pixel_position = 0; + double thumb_start_pixel_position = 0.0; Timer* mouse_down_timer = nullptr; - int last_step_size = 0; + double last_step_size = 0.0; Rect rect_button_decrement; Rect rect_track_decrement; @@ -91,7 +96,4 @@ private: Rect rect_button_increment; std::function* FuncScrollOnMouseDown = nullptr; - - static const int decr_height = 16; - static const int incr_height = 16; }; diff --git a/libraries/ZWidget/src/core/widget.cpp b/libraries/ZWidget/src/core/widget.cpp index 9a1082f48e..56d5e9ef57 100644 --- a/libraries/ZWidget/src/core/widget.cpp +++ b/libraries/ZWidget/src/core/widget.cpp @@ -419,7 +419,7 @@ Widget* Widget::ChildAt(const Point& pos) { if (cur->FrameGeometry.contains(pos)) { - Widget* cur2 = cur->ChildAt(pos - cur->FrameGeometry.topLeft()); + Widget* cur2 = cur->ChildAt(pos - cur->ContentGeometry.topLeft()); return cur2 ? cur2 : cur; } } diff --git a/libraries/ZWidget/src/widgets/listview/listview.cpp b/libraries/ZWidget/src/widgets/listview/listview.cpp index 6093d9d8e5..29346ad574 100644 --- a/libraries/ZWidget/src/widgets/listview/listview.cpp +++ b/libraries/ZWidget/src/widgets/listview/listview.cpp @@ -1,9 +1,13 @@ #include "widgets/listview/listview.h" +#include "widgets/scrollbar/scrollbar.h" ListView::ListView(Widget* parent) : Widget(parent) { - SetNoncontentSizes(10.0, 5.0, 10.0, 5.0); + SetNoncontentSizes(10.0, 10.0, 3.0, 10.0); + + scrollbar = new Scrollbar(this); + scrollbar->FuncScroll = [=]() { OnScrollbarScroll(); }; } void ListView::AddItem(const std::string& text) @@ -18,21 +22,53 @@ void ListView::Activate() OnActivated(); } +void ListView::ScrollToItem(int index) +{ + double itemHeight = 20.0; + double y = itemHeight * index; + if (y < scrollbar->GetPosition()) + { + scrollbar->SetPosition(y); + } + else if (y + itemHeight > scrollbar->GetPosition() + GetHeight()) + { + scrollbar->SetPosition(std::max(y + itemHeight - GetHeight(), 0.0)); + } +} + +void ListView::OnScrollbarScroll() +{ + Update(); +} + +void ListView::OnGeometryChanged() +{ + double w = GetWidth(); + double h = GetHeight(); + double sw = scrollbar->GetPreferredWidth(); + scrollbar->SetFrameGeometry(Rect::xywh(w - sw, 0.0, sw, h)); + scrollbar->SetRanges(h, items.size() * 20.0); +} + void ListView::OnPaint(Canvas* canvas) { - double y = 20.0; + double y = -scrollbar->GetPosition(); double x = 2.0; - double w = GetFrameGeometry().width; + double w = GetWidth() - scrollbar->GetPreferredWidth() - 2.0; double h = 20.0; int index = 0; for (const std::string& item : items) { - if (index == selectedItem) + double itemY = y; + if (itemY + h >= 0.0 && itemY < GetHeight()) { - canvas->fillRect(Rect::xywh(x - 2.0, y + 5.0 - h, w, h), Colorf::fromRgba8(100, 100, 100)); + if (index == selectedItem) + { + canvas->fillRect(Rect::xywh(x - 2.0, itemY, w, h), Colorf::fromRgba8(100, 100, 100)); + } + canvas->drawText(Point(x, y + 15.0), Colorf::fromRgba8(255, 255, 255), item); } - canvas->drawText(Point(x, y), Colorf::fromRgba8(255, 255, 255), item); y += h; index++; } @@ -56,11 +92,12 @@ void ListView::OnMouseDown(const Point& pos, int key) if (key == IK_LeftMouse) { - int index = (int)((pos.y - 5.0) / 20.0); + int index = (int)((pos.y - 5.0 + scrollbar->GetPosition()) / 20.0); if (index >= 0 && (size_t)index < items.size()) { selectedItem = index; Update(); + ScrollToItem(selectedItem); } } } @@ -73,6 +110,18 @@ void ListView::OnMouseDoubleclick(const Point& pos, int key) } } +void ListView::OnMouseWheel(const Point& pos, EInputKey key) +{ + if (key == IK_MouseWheelUp) + { + scrollbar->SetPosition(std::max(scrollbar->GetPosition() - 20.0, 0.0)); + } + else if (key == IK_MouseWheelDown) + { + scrollbar->SetPosition(std::max(scrollbar->GetPosition() + 20.0, scrollbar->GetMax())); + } +} + void ListView::OnKeyDown(EInputKey key) { if (key == IK_Down) @@ -82,6 +131,7 @@ void ListView::OnKeyDown(EInputKey key) selectedItem++; Update(); } + ScrollToItem(selectedItem); } else if (key == IK_Up) { @@ -90,6 +140,7 @@ void ListView::OnKeyDown(EInputKey key) selectedItem--; Update(); } + ScrollToItem(selectedItem); } else if (key == IK_Enter) { diff --git a/libraries/ZWidget/src/widgets/scrollbar/scrollbar.cpp b/libraries/ZWidget/src/widgets/scrollbar/scrollbar.cpp index eea72feabf..1462981630 100644 --- a/libraries/ZWidget/src/widgets/scrollbar/scrollbar.cpp +++ b/libraries/ZWidget/src/widgets/scrollbar/scrollbar.cpp @@ -25,27 +25,27 @@ bool Scrollbar::IsHorizontal() const return !vertical; } -int Scrollbar::GetMin() const +double Scrollbar::GetMin() const { return scroll_min; } -int Scrollbar::GetMax() const +double Scrollbar::GetMax() const { return scroll_max; } -int Scrollbar::GetLineStep() const +double Scrollbar::GetLineStep() const { return line_step; } -int Scrollbar::GetPageStep() const +double Scrollbar::GetPageStep() const { return page_step; } -int Scrollbar::GetPosition() const +double Scrollbar::GetPosition() const { return position; } @@ -64,61 +64,61 @@ void Scrollbar::SetHorizontal() Update(); } -void Scrollbar::SetMin(int new_scroll_min) +void Scrollbar::SetMin(double new_scroll_min) { SetRanges(new_scroll_min, scroll_max, line_step, page_step); } -void Scrollbar::SetMax(int new_scroll_max) +void Scrollbar::SetMax(double new_scroll_max) { SetRanges(scroll_min, new_scroll_max, line_step, page_step); } -void Scrollbar::SetLineStep(int step) +void Scrollbar::SetLineStep(double step) { SetRanges(scroll_min, scroll_max, step, page_step); } -void Scrollbar::SetPageStep(int step) +void Scrollbar::SetPageStep(double step) { SetRanges(scroll_min, scroll_max, line_step, step); } -void Scrollbar::SetRanges(int scroll_min, int scroll_max, int line_step, int page_step) +void Scrollbar::SetRanges(double new_scroll_min, double new_scroll_max, double new_line_step, double new_page_step) { - if (scroll_min >= scroll_max || line_step <= 0 || page_step <= 0) + if (new_scroll_min >= new_scroll_max || new_line_step <= 0.0 || new_page_step <= 0.0) throw std::runtime_error("Scrollbar ranges out of bounds!"); - scroll_min = scroll_min; - scroll_max = scroll_max; - line_step = line_step; - page_step = page_step; + scroll_min = new_scroll_min; + scroll_max = new_scroll_max; + line_step = new_line_step; + page_step = new_page_step; if (position >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (position < scroll_min) position = scroll_min; if (UpdatePartPositions()) Update(); } -void Scrollbar::SetRanges(int view_size, int total_size) +void Scrollbar::SetRanges(double view_size, double total_size) { - if (view_size <= 0 || total_size <= 0) + if (view_size <= 0.0 || total_size <= 0.0) { - SetRanges(0, 1, 1, 1); + SetRanges(0.0, 1.0, 1.0, 1.0); } else { - int scroll_max = std::max(1, total_size - view_size + 1); - int page_step = std::max(1, view_size); - SetRanges(0, scroll_max, 1, page_step); + double scroll_max = std::max(1.0, total_size - view_size + 1.0); + double page_step = std::max(1.0, view_size); + SetRanges(0.0, scroll_max, 10, page_step); } } -void Scrollbar::SetPosition(int pos) +void Scrollbar::SetPosition(double pos) { position = pos; if (pos >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (pos < scroll_min) position = scroll_min; @@ -130,24 +130,24 @@ void Scrollbar::OnMouseMove(const Point& pos) { if (mouse_down_mode == mouse_down_thumb_drag) { - int last_position = position; + double last_position = position; - if (pos.x < -100 || pos.x > GetWidth() + 100 || pos.y < -100 || pos.y > GetHeight() + 100) + if (pos.x < -100.0 || pos.x > GetWidth() + 100.0 || pos.y < -100.0 || pos.y > GetHeight() + 100.0) { position = thumb_start_position; } else { - int delta = (int)(vertical ? (pos.y - mouse_drag_start_pos.y) : (pos.x - mouse_drag_start_pos.x)); - int position_pixels = thumb_start_pixel_position + delta; + double delta = vertical ? (pos.y - mouse_drag_start_pos.y) : (pos.x - mouse_drag_start_pos.x); + double position_pixels = thumb_start_pixel_position + delta; - int track_height = 0; + double track_height = 0; if (vertical) - track_height = (int)(rect_track_decrement.height + rect_track_increment.height); + track_height = rect_track_decrement.height + rect_track_increment.height; else - track_height = (int)(rect_track_decrement.width + rect_track_increment.width); + track_height = rect_track_decrement.width + rect_track_increment.width; - if (track_height != 0) + if (track_height != 0.0) position = scroll_min + position_pixels * (scroll_max - scroll_min) / track_height; else position = 0; @@ -178,12 +178,12 @@ void Scrollbar::OnMouseDown(const Point& pos, int key) mouse_down_mode = mouse_down_button_decr; FuncScrollOnMouseDown = &FuncScrollLineDecrement; - int last_position = position; + double last_position = position; position -= line_step; last_step_size = -line_step; if (position >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (position < scroll_min) position = scroll_min; @@ -195,12 +195,12 @@ void Scrollbar::OnMouseDown(const Point& pos, int key) mouse_down_mode = mouse_down_button_incr; FuncScrollOnMouseDown = &FuncScrollLineIncrement; - int last_position = position; + double last_position = position; position += line_step; last_step_size = line_step; if (position >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (position < scroll_min) position = scroll_min; @@ -211,19 +211,19 @@ void Scrollbar::OnMouseDown(const Point& pos, int key) { mouse_down_mode = mouse_down_thumb_drag; thumb_start_position = position; - thumb_start_pixel_position = (int)(vertical ? (rect_thumb.y - rect_track_decrement.y) : (rect_thumb.x - rect_track_decrement.x)); + thumb_start_pixel_position = vertical ? (rect_thumb.y - rect_track_decrement.y) : (rect_thumb.x - rect_track_decrement.x); } else if (rect_track_decrement.contains(pos)) { mouse_down_mode = mouse_down_track_decr; FuncScrollOnMouseDown = &FuncScrollPageDecrement; - int last_position = position; + double last_position = position; position -= page_step; last_step_size = -page_step; if (position >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (position < scroll_min) position = scroll_min; @@ -235,12 +235,12 @@ void Scrollbar::OnMouseDown(const Point& pos, int key) mouse_down_mode = mouse_down_track_incr; FuncScrollOnMouseDown = &FuncScrollPageIncrement; - int last_position = position; + double last_position = position; position += page_step; last_step_size = page_step; if (position >= scroll_max) - position = scroll_max - 1; + position = scroll_max - 1.0; if (position < scroll_min) position = scroll_min; @@ -291,20 +291,26 @@ void Scrollbar::OnPaint(Canvas* canvas) part_track_increment.render_box(canvas, rect_track_increment); part_button_increment.render_box(canvas, rect_button_increment); */ + + canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), GetHeight()), 4.0, 0.0, 4.0, 0.0), Colorf::fromRgba8(33, 33, 33)); + canvas->fillRect(Rect::shrink(rect_thumb, 4.0, 0.0, 4.0, 0.0), Colorf::fromRgba8(58, 58, 58)); } // Calculates positions of all parts. Returns true if thumb position was changed compared to previously, false otherwise. bool Scrollbar::UpdatePartPositions() { - int total_height = (int)(vertical ? GetHeight() : GetWidth()); - int track_height = std::max(0, total_height - decr_height - incr_height); - int thumb_height = CalculateThumbSize(track_height); + double decr_height = showbuttons ? 16.0 : 0.0; + double incr_height = showbuttons ? 16.0 : 0.0; - int thumb_offset = decr_height + CalculateThumbPosition(thumb_height, track_height); + double total_height = vertical ? GetHeight() : GetWidth(); + double track_height = std::max(0.0, total_height - decr_height - incr_height); + double thumb_height = CalculateThumbSize(track_height); + + double thumb_offset = decr_height + CalculateThumbPosition(thumb_height, track_height); Rect previous_rect_thumb = rect_thumb; - rect_button_decrement = CreateRect(0, decr_height); + rect_button_decrement = CreateRect(0.0, decr_height); rect_track_decrement = CreateRect(decr_height, thumb_offset); rect_thumb = CreateRect(thumb_offset, thumb_offset + thumb_height); rect_track_increment = CreateRect(thumb_offset + thumb_height, decr_height + track_height); @@ -313,12 +319,12 @@ bool Scrollbar::UpdatePartPositions() return (previous_rect_thumb != rect_thumb); } -int Scrollbar::CalculateThumbSize(int track_size) +double Scrollbar::CalculateThumbSize(double track_size) { - int minimum_thumb_size = 20; - int range = scroll_max - scroll_min; - int length = range + page_step - 1; - int thumb_size = page_step * track_size / length; + double minimum_thumb_size = 20.0; + double range = scroll_max - scroll_min; + double length = range + page_step - 1; + double thumb_size = page_step * track_size / length; if (thumb_size < minimum_thumb_size) thumb_size = minimum_thumb_size; if (thumb_size > track_size) @@ -326,13 +332,13 @@ int Scrollbar::CalculateThumbSize(int track_size) return thumb_size; } -int Scrollbar::CalculateThumbPosition(int thumb_size, int track_size) +double Scrollbar::CalculateThumbPosition(double thumb_size, double track_size) { - int relative_pos = position - scroll_min; - int range = scroll_max - scroll_min - 1; + double relative_pos = position - scroll_min; + double range = scroll_max - scroll_min - 1; if (range != 0) { - int available_area = std::max(0, track_size - thumb_size); + double available_area = std::max(0.0, track_size - thumb_size); return relative_pos * available_area / range; } else @@ -341,7 +347,7 @@ int Scrollbar::CalculateThumbPosition(int thumb_size, int track_size) } } -Rect Scrollbar::CreateRect(int start, int end) +Rect Scrollbar::CreateRect(double start, double end) { if (vertical) return Rect(0.0, start, GetWidth(), end - start); @@ -356,7 +362,7 @@ void Scrollbar::OnTimerExpired() mouse_down_timer->Start(100, false); - int last_position = position; + double last_position = position; position += last_step_size; if (position >= scroll_max) position = scroll_max - 1; @@ -395,6 +401,6 @@ void Scrollbar::InvokeScrollEvent(std::function* event_ptr) if (FuncScroll) FuncScroll(); - if (event_ptr) + if (event_ptr && *event_ptr) (*event_ptr)(); } diff --git a/libraries/ZWidget/src/window/win32/win32window.cpp b/libraries/ZWidget/src/window/win32/win32window.cpp index e33bef4fdc..f76df67318 100644 --- a/libraries/ZWidget/src/window/win32/win32window.cpp +++ b/libraries/ZWidget/src/window/win32/win32window.cpp @@ -455,7 +455,15 @@ LRESULT Win32Window::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam) else if (msg == WM_MOUSEWHEEL) { double delta = GET_WHEEL_DELTA_WPARAM(wparam) / (double)WHEEL_DELTA; - WindowHost->OnWindowMouseWheel(GetLParamPos(lparam), delta < 0.0 ? IK_MouseWheelDown : IK_MouseWheelUp); + + // Note: WM_MOUSEWHEEL uses screen coordinates. GetLParamPos assumes client coordinates. + double dpiscale = GetDpiScale(); + POINT pos; + pos.x = GET_X_LPARAM(lparam); + pos.y = GET_Y_LPARAM(lparam); + ScreenToClient(WindowHandle, &pos); + + WindowHost->OnWindowMouseWheel(Point(pos.x / dpiscale, pos.y / dpiscale), delta < 0.0 ? IK_MouseWheelDown : IK_MouseWheelUp); } else if (msg == WM_CHAR) {