aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/platform/graphic_base.hpp12
-rw-r--r--include/cru/platform/native/native_window.hpp1
-rw-r--r--include/cru/ui/click_detector.hpp8
-rw-r--r--include/cru/ui/control.hpp12
-rw-r--r--include/cru/ui/controls/button.hpp10
-rw-r--r--include/cru/win/native/native_window.hpp1
-rw-r--r--src/ui/click_detector.cpp7
-rw-r--r--src/ui/control.cpp19
-rw-r--r--src/ui/controls/button.cpp84
-rw-r--r--src/ui/controls/flex_layout.cpp5
-rw-r--r--src/ui/controls/text_block.cpp1
-rw-r--r--src/ui/render/border_render_object.cpp2
-rw-r--r--src/ui/render/flex_layout_render_object.cpp2
-rw-r--r--src/ui/render/window_render_object.cpp2
-rw-r--r--src/ui/window.cpp8
-rw-r--r--src/win/native/native_window.cpp9
16 files changed, 132 insertions, 51 deletions
diff --git a/include/cru/platform/graphic_base.hpp b/include/cru/platform/graphic_base.hpp
index 2aa4e2cb..fd479e56 100644
--- a/include/cru/platform/graphic_base.hpp
+++ b/include/cru/platform/graphic_base.hpp
@@ -239,8 +239,14 @@ struct Color {
: red(red), green(green), blue(blue), alpha(alpha) {}
constexpr static Color FromHex(std::uint32_t hex) {
- return Color(hex & (0b11111111 << 16), hex & (0b11111111 << 8),
- hex & (0b11111111), hex & (0b11111111 << 24));
+ const std::uint32_t mask = 0b11111111;
+ return Color((hex >> 16) & mask, (hex >> 8) & mask, hex & mask, 255);
+ }
+
+ constexpr static Color FromHexAlpha(std::uint32_t hex) {
+ const std::uint32_t mask = 0b11111111;
+ return Color((hex >> 16) & mask, (hex >> 8) & mask, hex & mask,
+ (hex >> 24) & mask);
}
std::uint8_t red;
@@ -254,4 +260,4 @@ constexpr Color black{0, 0, 0};
constexpr Color white{255, 255, 255};
constexpr Color skyblue = Color::FromHex(0x87ceeb);
} // namespace colors
-} // namespace cru::ui
+} // namespace cru::platform
diff --git a/include/cru/platform/native/native_window.hpp b/include/cru/platform/native/native_window.hpp
index b557a0dd..d4e608b4 100644
--- a/include/cru/platform/native/native_window.hpp
+++ b/include/cru/platform/native/native_window.hpp
@@ -62,6 +62,7 @@ class NativeWindow : public NativeResource {
virtual bool CaptureMouse() = 0;
virtual bool ReleaseMouse() = 0;
+ virtual void Repaint() = 0;
virtual graph::Painter* BeginPaint() = 0;
virtual IEvent<std::nullptr_t>* DestroyEvent() = 0;
diff --git a/include/cru/ui/click_detector.hpp b/include/cru/ui/click_detector.hpp
index e7d5e417..3806dd82 100644
--- a/include/cru/ui/click_detector.hpp
+++ b/include/cru/ui/click_detector.hpp
@@ -47,7 +47,7 @@ class ClickDetector : public Object {
// Return a union of buttons being pressed. Return 0 if no button is being
// pressed.
- MouseButton IsPressing() const {
+ MouseButton GetPressingButton() const {
unsigned result = 0;
if (click_map_.left.has_value()) result |= MouseButton::Left;
if (click_map_.middle.has_value()) result |= MouseButton::Middle;
@@ -57,6 +57,9 @@ class ClickDetector : public Object {
IEvent<ClickEventArgs>* ClickEvent() { return &event_; }
+ IEvent<MouseButton>* ClickBeginEvent() { return &begin_event_; }
+ IEvent<MouseButton>* ClickEndEvent() { return &end_event_; }
+
private:
std::optional<Point>& FromButton(MouseButton button) {
switch (button) {
@@ -76,6 +79,9 @@ class ClickDetector : public Object {
Event<ClickEventArgs> event_;
+ Event<MouseButton> begin_event_;
+ Event<MouseButton> end_event_;
+
std::forward_list<EventRevokerGuard> event_rovoker_guards_;
struct {
diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp
index b39b59a0..f312272e 100644
--- a/include/cru/ui/control.hpp
+++ b/include/cru/ui/control.hpp
@@ -62,9 +62,11 @@ class Control : public Object {
public:
bool IsMouseOver() const { return is_mouse_over_; }
- bool CaptureMouse(); // TODO
+ bool CaptureMouse();
- bool IsMouseCaptured(); // TODO
+ bool ReleaseMouse();
+
+ bool IsMouseCaptured();
//*************** region: events ***************
public:
@@ -128,12 +130,6 @@ class Control : public Object {
virtual void OnAttachToWindow(Window* window);
virtual void OnDetachToWindow(Window* window);
- //*************** region: additional mouse event ***************
- protected:
- virtual void OnMouseClickBegin(platform::native::MouseButton button);
- virtual void OnMouseClickEnd(platform::native::MouseButton button);
- virtual void OnMouseClickCancel(platform::native::MouseButton button);
-
private:
Window* window_ = nullptr;
Control* parent_ = nullptr;
diff --git a/include/cru/ui/controls/button.hpp b/include/cru/ui/controls/button.hpp
index 648bb6bc..ca3dcae9 100644
--- a/include/cru/ui/controls/button.hpp
+++ b/include/cru/ui/controls/button.hpp
@@ -1,10 +1,10 @@
#pragma once
#include "../content_control.hpp"
+#include "../click_detector.hpp"
+#include "../render/border_render_object.hpp"
#include "cru/platform/graph/brush.hpp"
#include "cru/platform/native/basic_types.hpp"
-#include "cru/ui/base.hpp"
-#include "cru/ui/render/border_render_object.hpp"
#include <memory>
@@ -94,13 +94,11 @@ class Button : public ContentControl {
protected:
void OnChildChanged(Control* old_child, Control* new_child) override;
- void OnMouseClickBegin(platform::native::MouseButton button) override;
- void OnMouseClickEnd(platform::native::MouseButton button) override;
-
virtual void OnStateChange(ButtonState oldState, ButtonState newState);
private:
void SetState(ButtonState newState) {
+ if (state_ == newState) return;
const auto oldState = state_;
state_ = newState;
OnStateChange(oldState, newState);
@@ -114,5 +112,7 @@ class Button : public ContentControl {
MouseButton trigger_button_ = MouseButton::Left;
ButtonBorderStyle border_style_;
+
+ ClickDetector click_detector_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/win/native/native_window.hpp b/include/cru/win/native/native_window.hpp
index b044aef8..ccbdff40 100644
--- a/include/cru/win/native/native_window.hpp
+++ b/include/cru/win/native/native_window.hpp
@@ -53,6 +53,7 @@ class WinNativeWindow : public NativeWindow {
bool CaptureMouse() override;
bool ReleaseMouse() override;
+ void Repaint() override;
graph::Painter* BeginPaint() override;
IEvent<std::nullptr_t>* DestroyEvent() override { return &destroy_event_; }
diff --git a/src/ui/click_detector.cpp b/src/ui/click_detector.cpp
index 1442b885..b335f3b5 100644
--- a/src/ui/click_detector.cpp
+++ b/src/ui/click_detector.cpp
@@ -12,8 +12,9 @@ ClickDetector::ClickDetector(Control* control) {
EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler(
[this, control](event::MouseButtonEventArgs& args) {
if (!control->CaptureMouse()) return; // capture failed
- FromButton(args.GetMouseButton()) =
- args.GetPoint(); // save mouse down point
+ const auto button = args.GetMouseButton();
+ FromButton(button) = args.GetPoint(); // save mouse down point
+ begin_event_.Raise(button);
})));
event_rovoker_guards_.push_front(
@@ -25,8 +26,10 @@ ClickDetector::ClickDetector(Control* control) {
if (down_point.has_value()) {
event_.Raise(ClickEventArgs(control, down_point.value(),
args.GetPoint(), button));
+ end_event_.Raise(button);
down_point = std::nullopt;
}
+ control->ReleaseMouse();
})));
}
} // namespace cru::ui
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index d529b687..7bd33d84 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -54,6 +54,7 @@ void Control::_TraverseDescendants(
predicate(control);
for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate);
}
+
bool Control::RequestFocus() {
auto window = GetWindow();
if (window == nullptr) return false;
@@ -68,15 +69,21 @@ bool Control::HasFocus() {
return window->GetFocusControl() == this;
}
-void Control::OnParentChanged(Control* old_parent, Control* new_parent) {}
+bool Control::CaptureMouse() {
+ return GetWindow()->CaptureMouseFor(this);
+}
-void Control::OnAttachToWindow(Window* window) {}
+bool Control::ReleaseMouse() {
+ return GetWindow()->CaptureMouseFor(nullptr);
+}
-void Control::OnDetachToWindow(Window* window) {}
+bool Control::IsMouseCaptured() {
+ return GetWindow()->GetMouseCaptureControl() == this;
+}
-void Control::OnMouseClickBegin(platform::native::MouseButton button) {}
+void Control::OnParentChanged(Control* old_parent, Control* new_parent) {}
-void Control::OnMouseClickEnd(platform::native::MouseButton button) {}
+void Control::OnAttachToWindow(Window* window) {}
-void Control::OnMouseClickCancel(platform::native::MouseButton button) {}
+void Control::OnDetachToWindow(Window* window) {}
} // namespace cru::ui
diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp
index 618371fb..42a08e33 100644
--- a/src/ui/controls/button.cpp
+++ b/src/ui/controls/button.cpp
@@ -1,17 +1,60 @@
#include "cru/ui/controls/button.hpp"
+#include <memory>
+#include "cru/platform/graph/brush.hpp"
+#include "cru/platform/graph/graph_factory.hpp"
+#include "cru/platform/native/native_window.hpp"
#include "cru/ui/render/border_render_object.hpp"
#include "cru/ui/ui_manager.hpp"
+#include "cru/ui/window.hpp"
namespace cru::ui::controls {
-Button::Button() {
- const auto predefined_resource =
- UiManager::GetInstance()->GetPredefineResources();
- render_object_.reset(new render::BorderRenderObject(
- predefined_resource->button_normal_border_brush));
+Button::Button() : click_detector_(this) {
+ // const auto predefined_resource =
+ // UiManager::GetInstance()->GetPredefineResources();
+
+ const auto factory = platform::graph::GraphFactory::GetInstance();
+ border_style_.normal.brush = std::shared_ptr<platform::graph::Brush>(
+ factory->CreateSolidColorBrush(Color::FromHex(0x00bfff)));
+ border_style_.hover.brush = std::shared_ptr<platform::graph::Brush>(
+ factory->CreateSolidColorBrush(Color::FromHex(0x47d1ff)));
+ border_style_.press.brush = std::shared_ptr<platform::graph::Brush>(
+ factory->CreateSolidColorBrush(Color::FromHex(0x91e4ff)));
+
+ border_style_.normal.thickness = border_style_.hover.thickness =
+ border_style_.press.thickness = Thickness{3};
+
+ border_style_.normal.corner_radius = border_style_.hover.corner_radius =
+ border_style_.press.corner_radius = render::CornerRadius{Point{10, 5}};
+
+ render_object_.reset(
+ new render::BorderRenderObject(border_style_.normal.brush));
+ render_object_->SetAttachedControl(this);
render_object_->SetEnabled(true);
- render_object_->SetBorderWidth(Thickness{3});
- render_object_->SetCornerRadius(render::CornerRadius{Point{10, 5}});
+ render_object_->SetStyle(border_style_.normal);
+
+ MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs& args) {
+ if (click_detector_.GetPressingButton() & trigger_button_) {
+ SetState(ButtonState::Press);
+ } else {
+ SetState(ButtonState::Hover);
+ }
+ });
+
+ MouseLeaveEvent()->Direct()->AddHandler(
+ [this](event::MouseEventArgs& args) { SetState(ButtonState::Normal); });
+
+ click_detector_.ClickBeginEvent()->AddHandler([this](MouseButton button) {
+ if (button & trigger_button_) {
+ SetState(ButtonState::Press);
+ }
+ });
+
+ click_detector_.ClickEndEvent()->AddHandler([this](MouseButton button) {
+ if (button & trigger_button_) {
+ SetState(ButtonState::Normal);
+ }
+ });
}
render::RenderObject* Button::GetRenderObject() const {
@@ -24,21 +67,18 @@ void Button::OnChildChanged(Control* old_child, Control* new_child) {
render_object_->AddChild(new_child->GetRenderObject(), 0);
}
-void Button::OnMouseClickBegin(platform::native::MouseButton button) {
- if (button & trigger_button_) {
- SetState(ButtonState::Press);
- //TODO!
- }
-}
-
-void Button::OnMouseClickEnd(platform::native::MouseButton button) {
- if (button & trigger_button_) {
- SetState(ButtonState::Normal);
- //TODO!
- }
-}
-
void Button::OnStateChange(ButtonState oldState, ButtonState newState) {
-
+ switch (newState) {
+ case ButtonState::Normal:
+ render_object_->SetStyle(border_style_.normal);
+ break;
+ case ButtonState::Hover:
+ render_object_->SetStyle(border_style_.hover);
+ break;
+ case ButtonState::Press:
+ render_object_->SetStyle(border_style_.press);
+ break;
+ }
+ GetWindow()->GetNativeWindow()->Repaint();
}
} // namespace cru::ui::controls
diff --git a/src/ui/controls/flex_layout.cpp b/src/ui/controls/flex_layout.cpp
index 3b70c98c..340710cc 100644
--- a/src/ui/controls/flex_layout.cpp
+++ b/src/ui/controls/flex_layout.cpp
@@ -5,7 +5,10 @@
namespace cru::ui::controls {
using render::FlexLayoutRenderObject;
-FlexLayout::FlexLayout() { render_object_.reset(new FlexLayoutRenderObject()); }
+FlexLayout::FlexLayout() {
+ render_object_.reset(new FlexLayoutRenderObject());
+ render_object_->SetAttachedControl(this);
+}
render::RenderObject* FlexLayout::GetRenderObject() const {
return render_object_.get();
diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp
index 55d83acc..71903981 100644
--- a/src/ui/controls/text_block.cpp
+++ b/src/ui/controls/text_block.cpp
@@ -13,6 +13,7 @@ TextBlock::TextBlock() {
new TextRenderObject(predefined_resources->text_block_text_brush,
predefined_resources->text_block_font,
predefined_resources->text_block_selection_brush));
+ render_object_->SetAttachedControl(this);
}
render::RenderObject* TextBlock::GetRenderObject() const {
diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp
index 1bec3e0b..4b61e511 100644
--- a/src/ui/render/border_render_object.cpp
+++ b/src/ui/render/border_render_object.cpp
@@ -30,7 +30,7 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) {
if (const auto child = GetChild()) {
auto offset = child->GetOffset();
Point p{point.x - offset.x, point.y - offset.y};
- const auto result = child->HitTest(point);
+ const auto result = child->HitTest(p);
if (result != nullptr) {
return result;
}
diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp
index 8ce2db0a..5e313e49 100644
--- a/src/ui/render/flex_layout_render_object.cpp
+++ b/src/ui/render/flex_layout_render_object.cpp
@@ -29,7 +29,7 @@ RenderObject* FlexLayoutRenderObject::HitTest(const Point& point) {
for (auto i = children.crbegin(); i != children.crend(); ++i) {
auto offset = (*i)->GetOffset();
Point p{point.x - offset.x, point.y - offset.y};
- const auto result = (*i)->HitTest(point);
+ const auto result = (*i)->HitTest(p);
if (result != nullptr) {
return result;
}
diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp
index 89bb0beb..18faeee8 100644
--- a/src/ui/render/window_render_object.cpp
+++ b/src/ui/render/window_render_object.cpp
@@ -27,7 +27,7 @@ RenderObject* WindowRenderObject::HitTest(const Point& point) {
if (const auto child = GetChild()) {
auto offset = child->GetOffset();
Point p{point.x - offset.x, point.y - offset.y};
- const auto result = child->HitTest(point);
+ const auto result = child->HitTest(p);
if (result != nullptr) {
return result;
}
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index 47509749..1ec34c1d 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -7,6 +7,7 @@
#include "routed_event_dispatch.hpp"
#include <cassert>
+#include <list>
namespace cru::ui {
namespace {
@@ -54,6 +55,7 @@ Control* FindLowestCommonAncestor(Control* left, Control* right,
*find_result = in_right;
return result;
}
+ ++right_i;
}
return result;
}
@@ -64,6 +66,7 @@ Control* FindLowestCommonAncestor(Control* left, Control* right,
*find_result = in_left;
return result;
}
+ ++left_i;
}
return result;
}
@@ -75,13 +78,16 @@ Control* FindLowestCommonAncestor(Control* left, Control* right,
*find_result = in_right;
return result;
}
+ ++right_i;
}
while (left_i != left_list.cend()) {
if (*left_i == find_control) {
*find_result = in_left;
return result;
}
+ ++left_i;
}
+ return result;
}
++left_i;
++right_i;
@@ -107,9 +113,11 @@ Window::Window(tag_overlapped_constructor)
: mouse_hover_control_(nullptr),
focus_control_(this),
mouse_captured_control_(nullptr) {
+ window_ = this;
native_window_ =
platform::native::UiApplication::GetInstance()->CreateWindow(nullptr);
render_object_.reset(new render::WindowRenderObject(this));
+ render_object_->SetAttachedControl(this);
BindNativeEvent(this, native_window_->DestroyEvent(),
&Window::OnNativeDestroy, event_revoker_guards_);
diff --git a/src/win/native/native_window.cpp b/src/win/native/native_window.cpp
index 5da121cf..f301f955 100644
--- a/src/win/native/native_window.cpp
+++ b/src/win/native/native_window.cpp
@@ -147,6 +147,15 @@ bool WinNativeWindow::ReleaseMouse() {
return false;
}
+void WinNativeWindow::Repaint() {
+ if (IsValid()) {
+ if (!::InvalidateRect(hwnd_, nullptr, FALSE))
+ throw Win32Error(::GetLastError(), "Failed to invalidate window.");
+ if (!::UpdateWindow(hwnd_))
+ throw Win32Error(::GetLastError(), "Failed to update window.");
+ }
+}
+
graph::Painter* WinNativeWindow::BeginPaint() {
return new WindowD2DPainter(this);
}