aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui/host/WindowHost.hpp
blob: 6d338df1320e625408e4a1cf6fb8d9c2890cfe08 (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#pragma once
#include "../Base.hpp"

#include "../render/Base.hpp"
#include "cru/common/Event.hpp"
#include "cru/platform/gui/UiApplication.hpp"
#include "cru/platform/gui/Window.hpp"

#include <functional>
#include <memory>

namespace cru::ui::host {
class LayoutPaintCycler;

struct AfterLayoutEventArgs {};

struct CreateWindowParams {
  CreateWindowParams(platform::gui::INativeWindow* parent = nullptr,
                     platform::gui::CreateWindowFlag flag = {})
      : parent(parent), flag(flag) {}

  platform::gui::INativeWindow* parent;
  platform::gui::CreateWindowFlag flag;
};

// The bridge between control tree and native window.
class WindowHost : public Object {
  CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::host::WindowHost")

 public:
  WindowHost(controls::Control* root_control,
             CreateWindowParams create_window_params = {});

  CRU_DELETE_COPY(WindowHost)
  CRU_DELETE_MOVE(WindowHost)

  ~WindowHost() override;

 public:
  platform::gui::INativeWindow* GetNativeWindow() { return native_window_; }

  // Mark the layout as invalid, and arrange a re-layout later.
  // This method could be called more than one times in a message cycle. But
  // layout only takes place once.
  void InvalidateLayout();

  // Mark the paint as invalid, and arrange a re-paint later.
  // This method could be called more than one times in a message cycle. But
  // paint only takes place once.
  void InvalidatePaint();

  IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() {
    return &after_layout_event_;
  }

  void Relayout();
  void Relayout(const Size& available_size);

  void Repaint();

  // Is layout is invalid, wait for relayout and then run the action. Otherwist
  // run it right now.
  void RunAfterLayoutStable(std::function<void()> action);

  // If true, preferred size of root render object is set to window size when
  // measure. Default is true.
  bool IsLayoutPreferToFillWindow() const;
  void SetLayoutPreferToFillWindow(bool value);

  // Get current control that mouse hovers on. This ignores the mouse-capture
  // control. Even when mouse is captured by another control, this function
  // return the control under cursor. You can use `GetMouseCaptureControl` to
  // get more info.
  controls::Control* GetMouseHoverControl() const {
    return mouse_hover_control_;
  }

  //*************** region: focus ***************

  controls::Control* GetFocusControl();

  void SetFocusControl(controls::Control* control);

  //*************** region: focus ***************

  // Pass nullptr to release capture. If mouse is already capture by a control,
  // this capture will fail and return false. If control is identical to the
  // capturing control, capture is not changed and this function will return
  // true.
  //
  // When capturing control changes,
  // appropriate event will be sent. If mouse is not on the capturing control
  // and capture is released, mouse enter event will be sent to the mouse-hover
  // control. If mouse is not on the capturing control and capture is set, mouse
  // leave event will be sent to the mouse-hover control.
  bool CaptureMouseFor(controls::Control* control);

  // Return null if not captured.
  controls::Control* GetMouseCaptureControl();

  controls::Control* HitTest(const Point& point);

  void UpdateCursor();

 private:
  //*************** region: native messages ***************
  void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
  void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
  void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);

  void OnNativeFocus(platform::gui::INativeWindow* window,
                     cru::platform::gui::FocusChangeType focus);

  void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
                               cru::platform::gui::MouseEnterLeaveType enter);
  void OnNativeMouseMove(platform::gui::INativeWindow* window,
                         const Point& point);
  void OnNativeMouseDown(platform::gui::INativeWindow* window,
                         const platform::gui::NativeMouseButtonEventArgs& args);
  void OnNativeMouseUp(platform::gui::INativeWindow* window,
                       const platform::gui::NativeMouseButtonEventArgs& args);

  void OnNativeKeyDown(platform::gui::INativeWindow* window,
                       const platform::gui::NativeKeyEventArgs& args);
  void OnNativeKeyUp(platform::gui::INativeWindow* window,
                     const platform::gui::NativeKeyEventArgs& args);

  //*************** region: event dispatcher helper ***************

  void DispatchMouseHoverControlChangeEvent(controls::Control* old_control,
                                            controls::Control* new_control,
                                            const Point& point, bool no_leave,
                                            bool no_enter);

 private:
  controls::Control* root_control_ = nullptr;
  render::RenderObject* root_render_object_ = nullptr;

  platform::gui::INativeWindow* native_window_ = nullptr;

  std::unique_ptr<LayoutPaintCycler> layout_paint_cycler_;

  Event<AfterLayoutEventArgs> after_layout_event_;
  std::vector<std::function<void()> > after_layout_stable_action_;

  std::vector<EventRevokerGuard> event_revoker_guards_;

  controls::Control* mouse_hover_control_ = nullptr;

  controls::Control* focus_control_;

  controls::Control* mouse_captured_control_ = nullptr;

  bool layout_prefer_to_fill_window_ = true;
};
}  // namespace cru::ui::host