diff options
Diffstat (limited to 'src/ui/window.hpp')
-rw-r--r-- | src/ui/window.hpp | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/ui/window.hpp b/src/ui/window.hpp new file mode 100644 index 00000000..9f773f55 --- /dev/null +++ b/src/ui/window.hpp @@ -0,0 +1,302 @@ +#pragma once + +#include "system_headers.hpp" +#include <map> +#include <list> +#include <memory> + +#include "control.hpp" +#include "events/ui_event.hpp" + +namespace cru::graph +{ + class WindowRenderTarget; +} + +namespace cru::ui +{ + class WindowClass : public Object + { + public: + WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); + WindowClass(const WindowClass& other) = delete; + WindowClass(WindowClass&& other) = delete; + WindowClass& operator=(const WindowClass& other) = delete; + WindowClass& operator=(WindowClass&& other) = delete; + ~WindowClass() override = default; + + + const wchar_t* GetName() const + { + return name_.c_str(); + } + + ATOM GetAtom() const + { + return atom_; + } + + private: + String name_; + ATOM atom_; + }; + + class WindowManager : public Object + { + public: + WindowManager(); + WindowManager(const WindowManager& other) = delete; + WindowManager(WindowManager&& other) = delete; + WindowManager& operator=(const WindowManager& other) = delete; + WindowManager& operator=(WindowManager&& other) = delete; + ~WindowManager() override = default; + + + //Get the general window class for creating ordinary window. + WindowClass* GetGeneralWindowClass() const + { + return general_window_class_.get(); + } + + //Register a window newly created. + //This function adds the hwnd to hwnd-window map. + //It should be called immediately after a window was created. + void RegisterWindow(HWND hwnd, Window* window); + + //Unregister a window that is going to be destroyed. + //This function removes the hwnd from the hwnd-window map. + //It should be called immediately before a window is going to be destroyed, + void UnregisterWindow(HWND hwnd); + + //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. + Window* FromHandle(HWND hwnd); + + std::vector<Window*> GetAllWindows() const; + + private: + std::unique_ptr<WindowClass> general_window_class_; + std::map<HWND, Window*> window_map_; + }; + + + + class Window : public Control + { + friend class WindowManager; + public: + static constexpr auto control_type = L"Window"; + + Window(); + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; + + public: + StringView GetControlType() const override final; + + //*************** region: handle *************** + + //Get the handle of the window. Return null if window is invalid. + HWND GetWindowHandle() const + { + return hwnd_; + } + + //Return if the window is still valid, that is, hasn't been closed or destroyed. + bool IsWindowValid() const + { + return hwnd_ != nullptr; + } + + + //*************** region: window operations *************** + + //Close and destroy the window if the window is valid. + void Close(); + + //Send a repaint message to the window's message queue which may make the window repaint. + void Repaint() override; + + //Show the window. + void Show(); + + //Hide thw window. + void Hide(); + + //Get the client size. + Size GetClientSize(); + + //Set the client size and repaint. + void SetClientSize(const Size& size); + + //Get the rect of the window containing frame. + //The lefttop of the rect is relative to screen lefttop. + Rect GetWindowRect(); + + //Set the rect of the window containing frame. + //The lefttop of the rect is relative to screen lefttop. + void SetWindowRect(const Rect& rect); + + //Handle the raw window message. + //Return true if the message is handled and get the result through "result" argument. + //Return false if the message is not handled. + bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); + + //*************** region: mouse *************** + + Point GetMousePosition(); + + Control* GetMouseHoverControl() const + { + return mouse_hover_control_; + } + + //*************** region: position and size *************** + + //Always return (0, 0) for a window. + Point GetPositionRelative() override final; + + //This method has no effect for a window. + void SetPositionRelative(const Point& position) override final; + + //Get the size of client area for a window. + Size GetSize() override final; + + //This method has no effect for a window. Use SetClientSize instead. + void SetSize(const Size& size) override final; + + //*************** region: layout *************** + + void Relayout(); + + //*************** region: functions *************** + + //Refresh control list. + //It should be invoked every time a control is added or removed from the tree. + void RefreshControlList(); + + //Get the most top control at "point". + Control* HitTest(const Point& point); + + + //*************** region: focus *************** + + //Request focus for specified control. + bool RequestFocusFor(Control* control); + + //Get the control that has focus. + Control* GetFocusControl(); + + + //*************** region: mouse capture *************** + + Control* CaptureMouseFor(Control* control); + Control* ReleaseCurrentMouseCapture(); + + + //*************** region: cursor *************** + void UpdateCursor(); + + //*************** region: debug *************** +#ifdef CRU_DEBUG_LAYOUT + bool IsDebugLayout() const + { + return debug_layout_; + } + + void SetDebugLayout(bool value); +#endif + + public: + //*************** region: events *************** + events::UiEvent activated_event; + events::UiEvent deactivated_event; + + events::WindowNativeMessageEvent native_message_event; + + private: + //*************** region: native operations *************** + + //Get the client rect in pixel. + RECT GetClientRectPixel(); + + bool IsMessageInQueue(UINT message); + + void SetCursorInternal(HCURSOR cursor); + + + //*************** region: layout *************** + + Size OnMeasureContent(const Size& available_size) override; + + + //*************** region: native messages *************** + + void OnDestroyInternal(); + void OnPaintInternal(); + void OnResizeInternal(int new_width, int new_height); + + void OnSetFocusInternal(); + void OnKillFocusInternal(); + + void OnMouseMoveInternal(POINT point); + void OnMouseLeaveInternal(); + void OnMouseDownInternal(MouseButton button, POINT point); + void OnMouseUpInternal(MouseButton button, POINT point); + + void OnKeyDownInternal(int virtual_code); + void OnKeyUpInternal(int virtual_code); + void OnCharInternal(wchar_t c); + + void OnActivatedInternal(); + void OnDeactivatedInternal(); + + //*************** region: event dispatcher helper *************** + + template<typename EventArgs> + using EventMethod = void (Control::*)(EventArgs&); + + // Dispatch the event. + // + // This will invoke the "event_method" of the control and its parent and parent's + // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. + // + // Args is of type "EventArgs". The first init argument is "sender", which is + // automatically bound to each receiving control. The second init argument is + // "original_sender", which is unchanged. And "args" will be perfectly forwarded + // as the rest arguments. + template<typename EventArgs, typename... Args> + void DispatchEvent(Control* original_sender, EventMethod<EventArgs> event_method, Control* last_receiver, Args&&... args) + { + auto control = original_sender; + while (control != nullptr && control != last_receiver) + { + EventArgs event_args(control, original_sender, std::forward<Args>(args)...); + (control->*event_method)(event_args); + control = control->GetParent(); + } + } + + void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); + + private: + HWND hwnd_ = nullptr; + std::shared_ptr<graph::WindowRenderTarget> render_target_{}; + + std::list<Control*> control_list_{}; + + Control* mouse_hover_control_ = nullptr; + + bool window_focus_ = false; + Control* focus_control_ = this; // "focus_control_" can't be nullptr + + Control* mouse_capture_control_ = nullptr; + +#ifdef CRU_DEBUG_LAYOUT + bool debug_layout_ = false; +#endif + }; +} + |