aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base.hpp30
-rw-r--r--src/main.cpp13
-rw-r--r--src/math_util.hpp66
-rw-r--r--src/ui/control.cpp13
-rw-r--r--src/ui/control.hpp12
-rw-r--r--src/ui/controls/linear_layout.cpp12
-rw-r--r--src/ui/controls/scroll_control.cpp249
-rw-r--r--src/ui/controls/scroll_control.hpp87
-rw-r--r--src/ui/ui_base.hpp10
-rw-r--r--src/ui/ui_manager.cpp6
-rw-r--r--src/ui/ui_manager.hpp4
11 files changed, 437 insertions, 65 deletions
diff --git a/src/base.hpp b/src/base.hpp
index 5d8cb9ce..fdd736a0 100644
--- a/src/base.hpp
+++ b/src/base.hpp
@@ -8,9 +8,6 @@
#include <stdexcept>
#include <string_view>
#include <chrono>
-#include <optional>
-// ReSharper disable once CppUnusedIncludeDirective
-#include <type_traits>
namespace cru
{
@@ -60,31 +57,4 @@ namespace cru
if (!condition)
throw std::invalid_argument(error_message.data());
}
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::optional<T> min, const std::optional<T> max)
- {
- if (min.has_value() && n < min.value())
- return min.value();
- if (max.has_value() && n > max.value())
- return max.value();
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::nullopt_t, const std::optional<T> max)
- {
- if (max.has_value() && n > max.value())
- return max.value();
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::optional<T> min, const std::nullopt_t)
- {
- if (min.has_value() && n < min.value())
- return min.value();
- return n;
- }
-
}
diff --git a/src/main.cpp b/src/main.cpp
index 376c03b8..5cc98a72 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,6 +8,7 @@
#include "ui/controls/list_item.hpp"
#include "ui/controls/popup_menu.hpp"
#include "ui/controls/frame_layout.hpp"
+#include "ui/controls/scroll_control.hpp"
#include "graph/graph.hpp"
using cru::String;
@@ -27,6 +28,7 @@ using cru::ui::controls::Button;
using cru::ui::controls::TextBox;
using cru::ui::controls::ListItem;
using cru::ui::controls::FrameLayout;
+using cru::ui::controls::ScrollControl;
int APIENTRY wWinMain(
HINSTANCE hInstance,
@@ -183,9 +185,16 @@ int APIENTRY wWinMain(
}
{
- const auto text_block = CreateWithLayout<TextBlock>(LayoutSideParams::Stretch(), LayoutSideParams::Stretch(), L"This is a very very very very very long sentence!!!");
+ const auto scroll_view = CreateWithLayout<ScrollControl>(LayoutSideParams::Stretch(), LayoutSideParams::Stretch());
+
+ scroll_view->SetVerticalScrollBarVisibility(ScrollControl::ScrollBarVisibility::Always);
+
+ const auto text_block = TextBlock::Create(
+ L"Love myself I do. Not everything, but I love the good as well as the bad. I love my crazy lifestyle, and I love my hard discipline. I love my freedom of speech and the way my eyes get dark when I'm tired. I love that I have learned to trust people with my heart, even if it will get broken. I am proud of everything that I am and will become.");
text_block->SetSelectable(true);
- layout->AddChild(text_block);
+
+ scroll_view->AddChild(text_block);
+ layout->AddChild(scroll_view);
}
layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::Start), LayoutSideParams::Content(), L"This is a little short sentence!!!"));
diff --git a/src/math_util.hpp b/src/math_util.hpp
new file mode 100644
index 00000000..7b286346
--- /dev/null
+++ b/src/math_util.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+// ReSharper disable once CppUnusedIncludeDirective
+#include <type_traits>
+#include <optional>
+
+namespace cru
+{
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const std::optional<T> min, const std::optional<T> max)
+ {
+ if (min.has_value() && n < min.value())
+ return min.value();
+ if (max.has_value() && n > max.value())
+ return max.value();
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const T min, const T max)
+ {
+ if (n < min)
+ return min;
+ if (n > max)
+ return max;
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const std::nullopt_t, const std::optional<T> max)
+ {
+ if (max.has_value() && n > max.value())
+ return max.value();
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const std::optional<T> min, const std::nullopt_t)
+ {
+ if (min.has_value() && n < min.value())
+ return min.value();
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const std::nullopt_t, const T max)
+ {
+ if (n > max)
+ return max;
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ float Coerce(const T n, const T min, const std::nullopt_t)
+ {
+ if (n < min)
+ return min;
+ return n;
+ }
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ T AtLeast0(const T value)
+ {
+ return value < static_cast<T>(0) ? static_cast<T>(0) : value;
+ }
+}
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 66f22c58..32909c75 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -8,6 +8,7 @@
#include "exception.hpp"
#include "cru_debug.hpp"
#include "convert_util.hpp"
+#include "math_util.hpp"
#ifdef CRU_DEBUG_LAYOUT
#include "ui_manager.hpp"
@@ -316,6 +317,7 @@ namespace cru::ui
{
SetPositionRelative(rect.GetLeftTop());
SetSize(rect.GetSize());
+ AfterLayoutSelf();
OnLayoutCore(Rect(Point::Zero(), rect.GetSize()));
}
@@ -385,7 +387,7 @@ namespace cru::ui
void Control::UpdateBorder()
{
- RegenerateGeometries();
+ RegenerateGeometryInfo();
InvalidateLayout();
InvalidateDraw();
}
@@ -554,7 +556,7 @@ namespace cru::ui
void Control::OnSizeChangedCore(SizeChangedEventArgs & args)
{
- RegenerateGeometries();
+ RegenerateGeometryInfo();
#ifdef CRU_DEBUG_LAYOUT
margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder));
padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content));
@@ -575,7 +577,7 @@ namespace cru::ui
size_changed_event.Raise(args);
}
- void Control::RegenerateGeometries()
+ void Control::RegenerateGeometryInfo()
{
if (IsBordered())
{
@@ -1044,6 +1046,11 @@ namespace cru::ui
}
}
+ void Control::AfterLayoutSelf()
+ {
+
+ }
+
void Control::CheckAndNotifyPositionChanged()
{
if (this->old_position_ != this->position_)
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index 6c5b0ea5..1ce4afe3 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -32,7 +32,7 @@ namespace cru::ui
friend class Window;
friend class LayoutManager;
- private:
+ protected:
struct GeometryInfo
{
Microsoft::WRL::ComPtr<ID2D1Geometry> border_geometry = nullptr;
@@ -309,7 +309,12 @@ namespace cru::ui
void RaisePositionChangedEvent(events::PositionChangedEventArgs& args);
void RaiseSizeChangedEvent(events::SizeChangedEventArgs& args);
- void RegenerateGeometries();
+ void RegenerateGeometryInfo();
+
+ const GeometryInfo& GetGeometryInfo() const
+ {
+ return geometry_info_;
+ }
//*************** region: mouse event ***************
virtual void OnMouseEnter(events::MouseEventArgs& args);
@@ -366,6 +371,9 @@ namespace cru::ui
virtual Size OnMeasureContent(const Size& available_size);
virtual void OnLayoutContent(const Rect& rect);
+ // Called by Layout after set position and size.
+ virtual void AfterLayoutSelf();
+
private:
// Only for layout manager to use.
// Check if the old position is updated to current position.
diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp
index 3789b305..8fb91513 100644
--- a/src/ui/controls/linear_layout.cpp
+++ b/src/ui/controls/linear_layout.cpp
@@ -2,6 +2,8 @@
#include <algorithm>
+#include "math_util.hpp"
+
namespace cru::ui::controls
{
LinearLayout::LinearLayout(const Orientation orientation)
@@ -10,16 +12,6 @@ namespace cru::ui::controls
}
- inline float AtLeast0(const float value)
- {
- return value < 0 ? 0 : value;
- }
-
- inline Size AtLeast0(const Size& size)
- {
- return Size(AtLeast0(size.width), AtLeast0(size.height));
- }
-
StringView LinearLayout::GetControlType() const
{
return control_type;
diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp
index 6b6d899c..103a5c20 100644
--- a/src/ui/controls/scroll_control.cpp
+++ b/src/ui/controls/scroll_control.cpp
@@ -4,9 +4,16 @@
#include "cru_debug.hpp"
#include "format.hpp"
+#include "ui/convert_util.hpp"
+#include "exception.hpp"
+#include "math_util.hpp"
+#include "ui/ui_manager.hpp"
+#include "ui/window.hpp"
namespace cru::ui::controls
{
+ constexpr auto scroll_bar_width = 15.0f;
+
ScrollControl::ScrollControl(const bool container) : Control(container)
{
SetClipContent(true);
@@ -17,6 +24,11 @@ namespace cru::ui::controls
}
+ StringView ScrollControl::GetControlType() const
+ {
+ return control_type;
+ }
+
void ScrollControl::SetHorizontalScrollEnabled(const bool enable)
{
horizontal_scroll_enabled_ = enable;
@@ -31,9 +43,50 @@ namespace cru::ui::controls
InvalidateDraw();
}
- Control* ScrollControl::HitTest(const Point& point)
+ void ScrollControl::SetHorizontalScrollBarVisibility(const ScrollBarVisibility visibility)
{
+ if (visibility != horizontal_scroll_bar_visibility_)
+ {
+ horizontal_scroll_bar_visibility_ = visibility;
+ switch (visibility)
+ {
+ case ScrollBarVisibility::Always:
+ is_horizontal_scroll_bar_visible_ = true;
+ break;
+ case ScrollBarVisibility::None:
+ is_horizontal_scroll_bar_visible_ = false;
+ break;
+ case ScrollBarVisibility::Auto:
+ UpdateScrollBarVisibility();
+ }
+ InvalidateDraw();
+ }
+ }
+ void ScrollControl::SetVerticalScrollBarVisibility(const ScrollBarVisibility visibility)
+ {
+ if (visibility != vertical_scroll_bar_visibility_)
+ {
+ vertical_scroll_bar_visibility_ = visibility;
+ switch (visibility)
+ {
+ case ScrollBarVisibility::Always:
+ is_vertical_scroll_bar_visible_ = true;
+ break;
+ case ScrollBarVisibility::None:
+ is_vertical_scroll_bar_visible_ = false;
+ break;
+ case ScrollBarVisibility::Auto:
+ UpdateScrollBarVisibility();
+ }
+ InvalidateDraw();
+ }
+
+ }
+
+ void ScrollControl::SetScrollOffset(std::optional<float> x, std::optional<float> y)
+ {
+ CoerceAndSetOffsets(x.value_or(GetScrollOffsetX()), y.value_or(GetScrollOffsetY()));
}
void ScrollControl::SetViewWidth(const float length)
@@ -54,13 +107,13 @@ namespace cru::ui::controls
if (IsHorizontalScrollEnabled())
{
if (layout_params->width.mode == MeasureMode::Content)
- debug::DebugMessage(L"ScrollView: Width measure mode is Content and horizontal scroll is enabled. So Stretch is used instead.");
+ debug::DebugMessage(L"ScrollControl: Width measure mode is Content and horizontal scroll is enabled. So Stretch is used instead.");
for (auto child : GetChildren())
{
const auto child_layout_params = child->GetLayoutParams();
if (child_layout_params->width.mode == MeasureMode::Stretch)
- throw std::runtime_error(Format("ScrollView: Horizontal scroll is enabled but a child {} 's width measure mode is Stretch which may cause infinite length.", ToUtf8String(child->GetControlType())));
+ throw std::runtime_error(Format("ScrollControl: Horizontal scroll is enabled but a child {} 's width measure mode is Stretch which may cause infinite length.", ToUtf8String(child->GetControlType())));
}
available_size_for_children.width = std::numeric_limits<float>::max();
@@ -69,13 +122,13 @@ namespace cru::ui::controls
if (IsVerticalScrollEnabled())
{
if (layout_params->height.mode == MeasureMode::Content)
- debug::DebugMessage(L"ScrollView: Height measure mode is Content and vertical scroll is enabled. So Stretch is used instead.");
+ debug::DebugMessage(L"ScrollControl: Height measure mode is Content and vertical scroll is enabled. So Stretch is used instead.");
for (auto child : GetChildren())
{
const auto child_layout_params = child->GetLayoutParams();
if (child_layout_params->height.mode == MeasureMode::Stretch)
- throw std::runtime_error(Format("ScrollView: Vertical scroll is enabled but a child {} 's height measure mode is Stretch which may cause infinite length.", ToUtf8String(child->GetControlType())));
+ throw std::runtime_error(Format("ScrollControl: Vertical scroll is enabled but a child {} 's height measure mode is Stretch which may cause infinite length.", ToUtf8String(child->GetControlType())));
}
available_size_for_children.height = std::numeric_limits<float>::max();
@@ -132,15 +185,193 @@ namespace cru::ui::controls
{
const auto size = control->GetDesiredSize();
// Ignore alignment, always center aligned.
- auto&& calculate_anchor = [](const float anchor, const float layout_length, const float control_length) -> float
+ auto&& calculate_anchor = [](const float anchor, const float layout_length, const float control_length, const float offset) -> float
{
- return anchor + (layout_length - control_length) / 2;
+ return anchor + (layout_length - control_length) / 2 - offset;
};
control->Layout(Rect(Point(
- calculate_anchor(rect.left, layout_rect.width, size.width),
- calculate_anchor(rect.top, layout_rect.height, size.height)
+ calculate_anchor(rect.left, layout_rect.width, size.width, offset_x_),
+ calculate_anchor(rect.top, layout_rect.height, size.height, offset_y_)
), size));
}
}
+
+ void ScrollControl::AfterLayoutSelf()
+ {
+ UpdateScrollBarBorderInfo();
+ CoerceAndSetOffsets(offset_x_, offset_y_, false);
+ UpdateScrollBarVisibility();
+ }
+
+ void ScrollControl::OnDrawForeground(ID2D1DeviceContext* device_context)
+ {
+ Control::OnDrawForeground(device_context);
+
+ const auto predefined = UiManager::GetInstance()->GetPredefineResources();
+
+ if (is_horizontal_scroll_bar_visible_)
+ {
+ device_context->FillRectangle(
+ Convert(horizontal_bar_info_.border),
+ predefined->scroll_bar_background_brush.Get()
+ );
+
+ device_context->FillRectangle(
+ Convert(horizontal_bar_info_.bar),
+ predefined->scroll_bar_brush.Get()
+ );
+
+ device_context->DrawLine(
+ Convert(horizontal_bar_info_.border.GetLeftTop()),
+ Convert(horizontal_bar_info_.border.GetRightTop()),
+ predefined->scroll_bar_border_brush.Get()
+ );
+ }
+
+ if (is_vertical_scroll_bar_visible_)
+ {
+ device_context->FillRectangle(
+ Convert(vertical_bar_info_.border),
+ predefined->scroll_bar_background_brush.Get()
+ );
+
+ device_context->FillRectangle(
+ Convert(vertical_bar_info_.bar),
+ predefined->scroll_bar_brush.Get()
+ );
+
+ device_context->DrawLine(
+ Convert(vertical_bar_info_.border.GetLeftTop()),
+ Convert(vertical_bar_info_.border.GetLeftBottom()),
+ predefined->scroll_bar_border_brush.Get()
+ );
+ }
+ }
+
+ void ScrollControl::OnMouseDownCore(events::MouseButtonEventArgs& args)
+ {
+ Control::OnMouseDownCore(args);
+
+ if (args.GetMouseButton() == MouseButton::Left)
+ {
+ const auto point = args.GetPoint(this);
+ if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point))
+ {
+ GetWindow()->CaptureMouseFor(this);
+ is_pressing_scroll_bar_ = Orientation::Vertical;
+ pressing_delta_ = point.y - vertical_bar_info_.bar.top;
+ return;
+ }
+
+ if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point))
+ {
+ GetWindow()->CaptureMouseFor(this);
+ pressing_delta_ = point.x - horizontal_bar_info_.bar.left;
+ is_pressing_scroll_bar_ = Orientation::Horizontal;
+ return;
+ }
+ }
+ }
+
+ void ScrollControl::OnMouseMoveCore(events::MouseEventArgs& args)
+ {
+ Control::OnMouseMoveCore(args);
+
+ const auto mouse_point = args.GetPoint(this);
+
+ if (is_pressing_scroll_bar_ == Orientation::Horizontal)
+ {
+ const auto new_head_position = mouse_point.x - pressing_delta_;
+ const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_;
+ SetScrollOffset(new_offset, std::nullopt);
+ return;
+ }
+
+ if (is_pressing_scroll_bar_ == Orientation::Vertical)
+ {
+ const auto new_head_position = mouse_point.y - pressing_delta_;
+ const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_;
+ SetScrollOffset(std::nullopt, new_offset);
+ return;
+ }
+ }
+
+ void ScrollControl::OnMouseUpCore(events::MouseButtonEventArgs& args)
+ {
+ Control::OnMouseUpCore(args);
+
+ if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value())
+ {
+ GetWindow()->ReleaseCurrentMouseCapture();
+ is_pressing_scroll_bar_ = std::nullopt;
+ }
+ }
+
+ void ScrollControl::CoerceAndSetOffsets(const float offset_x, const float offset_y, const bool update_children)
+ {
+ const auto old_offset_x = offset_x_;
+ const auto old_offset_y = offset_y_;
+
+ const auto content_rect = GetRect(RectRange::Content);
+ offset_x_ = Coerce(offset_x, 0.0f, AtLeast0(view_width_ - content_rect.width));
+ offset_y_ = Coerce(offset_y, 0.0f, AtLeast0(view_height_ - content_rect.height));
+ UpdateScrollBarBarInfo();
+
+ if (update_children)
+ {
+ for (auto child : GetChildren())
+ {
+ const auto old_position = child->GetPositionRelative();
+ child->SetPositionRelative(Point(
+ old_position.x + old_offset_x - offset_x_,
+ old_position.y + old_offset_y - offset_y_
+ ));
+ }
+ }
+ InvalidateDraw();
+ }
+
+ void ScrollControl::UpdateScrollBarVisibility()
+ {
+ const auto content_rect = GetRect(RectRange::Content);
+ if (GetHorizontalScrollBarVisibility() == ScrollBarVisibility::Auto)
+ is_horizontal_scroll_bar_visible_ = view_width_ > content_rect.width;
+ if (GetVerticalScrollBarVisibility() == ScrollBarVisibility::Auto)
+ is_vertical_scroll_bar_visible_ = view_height_ > content_rect.height;
+ }
+
+ void ScrollControl::UpdateScrollBarBorderInfo()
+ {
+ const auto content_rect = GetRect(RectRange::Content);
+ horizontal_bar_info_.border = Rect(content_rect.left, content_rect.GetBottom() - scroll_bar_width, content_rect.width, scroll_bar_width);
+ vertical_bar_info_.border = Rect(content_rect.GetRight() - scroll_bar_width , content_rect.top, scroll_bar_width, content_rect.height);
+ }
+
+ void ScrollControl::UpdateScrollBarBarInfo()
+ {
+ const auto content_rect = GetRect(RectRange::Content);
+ {
+ const auto& border = horizontal_bar_info_.border;
+ if (view_width_ <= content_rect.width)
+ horizontal_bar_info_.bar = border;
+ else
+ {
+ const auto bar_length = border.width * content_rect.width / view_width_;
+ const auto offset = border.width * offset_x_ / view_width_;
+ horizontal_bar_info_.bar = Rect(border.left + offset, border.top, bar_length, border.height);
+ }
+ }
+ {
+ const auto& border = vertical_bar_info_.border;
+ if (view_height_ <= content_rect.height)
+ vertical_bar_info_.bar = border;
+ else
+ {
+ const auto bar_length = border.height * content_rect.height / view_height_;
+ const auto offset = border.height * offset_y_ / view_height_;
+ vertical_bar_info_.bar = Rect(border.left, border.top + offset, border.width, bar_length);
+ }
+ }
+ }
}
diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp
index c39b18f4..faf192ad 100644
--- a/src/ui/controls/scroll_control.hpp
+++ b/src/ui/controls/scroll_control.hpp
@@ -1,17 +1,34 @@
#pragma once
+#include <optional>
+#include <initializer_list>
+
#include "ui/control.hpp"
namespace cru::ui::controls
{
// Done: OnMeasureContent
// Done: OnLayoutContent
- // TODO: HitTest
- // TODO: Draw
- // TODO: ScrollBar
+ // Done: HitTest(no need)
+ // Done: Draw(no need)
+ // Done: API
+ // Done: ScrollBar
// TODO: MouseEvent
class ScrollControl : public Control
{
+ private:
+ struct ScrollBarInfo
+ {
+ Rect border = Rect();
+ Rect bar = Rect();
+ };
+
+ enum class Orientation
+ {
+ Horizontal,
+ Vertical
+ };
+
public:
enum class ScrollBarVisibility
{
@@ -20,6 +37,16 @@ namespace cru::ui::controls
Always
};
+ static ScrollControl* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>{})
+ {
+ const auto control = new ScrollControl(true);
+ for (auto child : children)
+ control->AddChild(child);
+ return control;
+ }
+
+ static constexpr auto control_type = L"ScrollControl";
+
protected:
explicit ScrollControl(bool container);
public:
@@ -29,6 +56,7 @@ namespace cru::ui::controls
ScrollControl& operator=(ScrollControl&& other) = delete;
~ScrollControl() override;
+ StringView GetControlType() const override final;
bool IsHorizontalScrollEnabled() const
{
@@ -45,14 +73,20 @@ namespace cru::ui::controls
void SetVerticalScrollEnabled(bool enable);
- ScrollBarVisibility GetHorizontalScrollBarVisibility() const;
+ ScrollBarVisibility GetHorizontalScrollBarVisibility() const
+ {
+ return horizontal_scroll_bar_visibility_;
+ }
+
void SetHorizontalScrollBarVisibility(ScrollBarVisibility visibility);
- ScrollBarVisibility GetVerticalScrollBarVisibility() const;
- void SetVerticalScrollBarVisibility(ScrollBarVisibility visibility);
- Control* HitTest(const Point& point) override final;
+ ScrollBarVisibility GetVerticalScrollBarVisibility() const
+ {
+ return vertical_scroll_bar_visibility_;
+ }
+
+ void SetVerticalScrollBarVisibility(ScrollBarVisibility visibility);
- protected:
float GetViewWidth() const
{
return view_width_;
@@ -63,12 +97,40 @@ namespace cru::ui::controls
return view_height_;
}
+ float GetScrollOffsetX() const
+ {
+ return offset_x_;
+ }
+
+ float GetScrollOffsetY() const
+ {
+ return offset_y_;
+ }
+
+ // nullopt for not set. value is auto-coerced.
+ void SetScrollOffset(std::optional<float> x, std::optional<float> y);
+
+ protected:
void SetViewWidth(float length);
void SetViewHeight(float length);
Size OnMeasureContent(const Size& available_size) override final;
void OnLayoutContent(const Rect& rect) override final;
+ void AfterLayoutSelf() override;
+
+ void OnDrawForeground(ID2D1DeviceContext* device_context) override;
+
+ void OnMouseDownCore(events::MouseButtonEventArgs& args) override final;
+ void OnMouseMoveCore(events::MouseEventArgs& args) override final;
+ void OnMouseUpCore(events::MouseButtonEventArgs& args) override final;
+
+ private:
+ void CoerceAndSetOffsets(float offset_x, float offset_y, bool update_children = true);
+ void UpdateScrollBarVisibility();
+ void UpdateScrollBarBorderInfo();
+ void UpdateScrollBarBarInfo();
+
private:
bool horizontal_scroll_enabled_ = true;
bool vertical_scroll_enabled_ = true;
@@ -76,10 +138,19 @@ namespace cru::ui::controls
ScrollBarVisibility horizontal_scroll_bar_visibility_ = ScrollBarVisibility::Auto;
ScrollBarVisibility vertical_scroll_bar_visibility_ = ScrollBarVisibility::Auto;
+ bool is_horizontal_scroll_bar_visible_ = false;
+ bool is_vertical_scroll_bar_visible_ = false;
+
float offset_x_ = 0.0f;
float offset_y_ = 0.0f;
float view_width_ = 0.0f;
float view_height_ = 0.0f;
+
+ ScrollBarInfo horizontal_bar_info_;
+ ScrollBarInfo vertical_bar_info_;
+
+ std::optional<Orientation> is_pressing_scroll_bar_ = std::nullopt;
+ float pressing_delta_ = 0.0f;
};
}
diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp
index d9c9d0b2..aaba343e 100644
--- a/src/ui/ui_base.hpp
+++ b/src/ui/ui_base.hpp
@@ -150,6 +150,16 @@ namespace cru::ui
return Point(left + width, top + height);
}
+ constexpr Point GetLeftBottom() const
+ {
+ return Point(left, top + height);
+ }
+
+ constexpr Point GetRightTop() const
+ {
+ return Point(left + width, top);
+ }
+
constexpr Size GetSize() const
{
return Size(width, height);
diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp
index 36fb2fb0..689a04a2 100644
--- a/src/ui/ui_manager.cpp
+++ b/src/ui/ui_manager.cpp
@@ -75,7 +75,11 @@ namespace cru::ui
list_item_hover_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue))},
list_item_hover_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))},
list_item_select_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::MediumBlue))},
- list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))}
+ list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))},
+
+ scroll_bar_background_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Gainsboro, 0.3f))},
+ scroll_bar_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))},
+ scroll_bar_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))}
#ifdef CRU_DEBUG_LAYOUT
,
diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp
index 6b368e12..9ad68eff 100644
--- a/src/ui/ui_manager.hpp
+++ b/src/ui/ui_manager.hpp
@@ -61,6 +61,10 @@ namespace cru::ui
Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_border_brush;
Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_fill_brush;
+ //region ScrollControl
+ Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_background_brush;
+ Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_border_brush;
+ Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_brush;
#ifdef CRU_DEBUG_LAYOUT
//region debug