aboutsummaryrefslogtreecommitdiff
path: root/src/ui/control.cpp
blob: 8e8819de6f55c6538f0f6103c9d087a2d195b645 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include "cru/ui/control.hpp"

#include "cru/platform/native/basic_types.hpp"
#include "cru/platform/native/cursor.hpp"
#include "cru/platform/native/native_window.hpp"
#include "cru/platform/native/ui_application.hpp"
#include "cru/ui/base.hpp"
#include "cru/ui/event/ui_event.hpp"
#include "cru/ui/window.hpp"
#include "routed_event_dispatch.hpp"

#include <cassert>
#include <memory>

namespace cru::ui {
using platform::native::Cursor;
using platform::native::SystemCursor;
using platform::native::UiApplication;

Control::Control() {
  MouseEnterEvent()->Direct()->AddHandler(
      [this](event::MouseEventArgs&) { this->is_mouse_over_ = true; });

  MouseLeaveEvent()->Direct()->AddHandler(
      [this](event::MouseEventArgs&) { this->is_mouse_over_ = false; });
}

void Control::_SetParent(Control* parent) {
  const auto old_parent = GetParent();
  parent_ = parent;
  const auto new_parent = GetParent();
  if (old_parent != new_parent) OnParentChanged(old_parent, new_parent);
}

void Control::_SetDescendantWindow(Window* window) {
  if (window == nullptr && window_ == nullptr) return;

  // You can only attach or detach window.
  assert((window != nullptr && window_ == nullptr) ||
         (window == nullptr && window_ != nullptr));

  if (window == nullptr) {
    const auto old = window_;
    TraverseDescendants([old](Control* control) {
      control->window_ = nullptr;
      control->OnDetachToWindow(old);
    });
  } else
    TraverseDescendants([window](Control* control) {
      control->window_ = window;
      control->OnAttachToWindow(window);
    });
}

void Control::TraverseDescendants(
    const std::function<void(Control*)>& predicate) {
  _TraverseDescendants(this, predicate);
}

void Control::_TraverseDescendants(
    Control* control, const std::function<void(Control*)>& predicate) {
  predicate(control);
  for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate);
}

bool Control::RequestFocus() {
  auto window = GetWindow();
  if (window == nullptr) return false;

  return window->RequestFocusFor(this);
}

bool Control::HasFocus() {
  auto window = GetWindow();
  if (window == nullptr) return false;

  return window->GetFocusControl() == this;
}

bool Control::CaptureMouse() { return GetWindow()->CaptureMouseFor(this); }

bool Control::ReleaseMouse() { return GetWindow()->CaptureMouseFor(nullptr); }

bool Control::IsMouseCaptured() {
  return GetWindow()->GetMouseCaptureControl() == this;
}

std::shared_ptr<Cursor> Control::GetCursor() { return cursor_; }

std::shared_ptr<Cursor> Control::GetInheritedCursor() {
  Control* control = this;
  while (control != nullptr) {
    const auto cursor = control->GetCursor();
    if (cursor != nullptr) return cursor;
    control = control->GetParent();
  }
  return UiApplication::GetInstance()->GetCursorManager()->GetSystemCursor(
      SystemCursor::Arrow);
}

void Control::SetCursor(std::shared_ptr<Cursor> cursor) {
  cursor_ = std::move(cursor);
  const auto window = GetWindow();
  if (window != nullptr) {
    window->UpdateCursor();
  }
}

void Control::OnParentChanged(Control* old_parent, Control* new_parent) {}

void Control::OnAttachToWindow(Window* window) {}

void Control::OnDetachToWindow(Window* window) {}
}  // namespace cru::ui