#pragma once #include "system_headers.hpp" #include #include #include #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: static WindowManager* GetInstance(); private: WindowManager(); public: 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 GetAllWindows() const; private: std::unique_ptr general_window_class_; std::map window_map_; }; class Window final : public Control { friend class WindowManager; public: static constexpr auto control_type = L"Window"; public: static Window* CreateOverlapped(); static Window* CreatePopup(Window* parent, bool caption = false); private: struct tag_overlapped_constructor {}; struct tag_popup_constructor {}; explicit Window(tag_overlapped_constructor); Window(tag_popup_constructor, Window* parent, bool caption); void AfterCreateHwnd(WindowManager* window_manager); public: 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; void SetDeleteThisOnDestroy(bool value); //*************** 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 *************** Window* GetParentWindow() const { return parent_window_; } //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); //Set the lefttop of the window relative to screen. void SetWindowPosition(const Point& position); Point PointToScreen(const Point& point); Point PointFromScreen(const Point& point); //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 WindowInvalidateLayout(); void Relayout(); void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); //*************** 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: 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 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 void DispatchEvent(Control* original_sender, EventMethod 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)...); (control->*event_method)(event_args); control = control->GetParent(); } } void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); private: bool delete_this_on_destroy_ = true; HWND hwnd_ = nullptr; Window* parent_window_ = nullptr; std::shared_ptr render_target_{}; std::list 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; bool is_layout_invalid_ = false; #ifdef CRU_DEBUG_LAYOUT bool debug_layout_ = false; #endif }; }