#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class WSScreen; class WSMouseEvent; class WSClientWantsToPaintMessage; class WSWindow; class WSClientConnection; class WSWindowSwitcher; class GraphicsBitmap; class WSButton; enum class ResizeDirection { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft }; class WSWindowManager : public CObject { C_OBJECT(WSWindowManager) friend class WSCompositor; friend class WSWindowFrame; friend class WSWindowSwitcher; public: static WSWindowManager& the(); explicit WSWindowManager(const PaletteImpl&); virtual ~WSWindowManager() override; Palette palette() const { return Palette(*m_palette); } RefPtr wm_config() const { return m_wm_config; } void reload_config(bool); void add_window(WSWindow&); void remove_window(WSWindow&); void notify_title_changed(WSWindow&); void notify_rect_changed(WSWindow&, const Rect& oldRect, const Rect& newRect); void notify_minimization_state_changed(WSWindow&); void notify_opacity_changed(WSWindow&); void notify_occlusion_state_changed(WSWindow&); void notify_client_changed_app_menubar(WSClientConnection&); Rect maximized_window_rect(const WSWindow&) const; WSClientConnection* dnd_client() { return m_dnd_client.ptr(); } const String& dnd_text() const { return m_dnd_text; } const String& dnd_data_type() const { return m_dnd_data_type; } const String& dnd_data() const { return m_dnd_data; } const GraphicsBitmap* dnd_bitmap() const { return m_dnd_bitmap; } Rect dnd_rect() const; void start_dnd_drag(WSClientConnection&, const String& text, GraphicsBitmap*, const String& data_type, const String& data); void end_dnd_drag(); WSWindow* active_window() { return m_active_window.ptr(); } const WSClientConnection* active_client() const; bool active_window_is_modal() const { return m_active_window && m_active_window->is_modal(); } WSWindow* highlight_window() { return m_highlight_window.ptr(); } void set_highlight_window(WSWindow*); void move_to_front_and_make_active(WSWindow&); void draw_window_switcher(); Rect menubar_rect() const; const WSCursor& active_cursor() const; const WSCursor& arrow_cursor() const { return *m_arrow_cursor; } const WSCursor& hand_cursor() const { return *m_hand_cursor; } const WSCursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; } const WSCursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; } const WSCursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; } const WSCursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; } const WSCursor& i_beam_cursor() const { return *m_i_beam_cursor; } const WSCursor& disallowed_cursor() const { return *m_disallowed_cursor; } const WSCursor& move_cursor() const { return *m_move_cursor; } const WSCursor& drag_cursor() const { return *m_drag_cursor; } void invalidate(const WSWindow&); void invalidate(const WSWindow&, const Rect&); void invalidate(const Rect&); void invalidate(); void flush(const Rect&); const Font& font() const; const Font& window_title_font() const; const Font& menu_font() const; const Font& app_menu_font() const; int menubar_menu_margin() const; void set_resolution(int width, int height); void set_active_window(WSWindow*); void set_hovered_button(WSButton*); WSButton* cursor_tracking_button() { return m_cursor_tracking_button.ptr(); } void set_cursor_tracking_button(WSButton*); void set_resize_candidate(WSWindow&, ResizeDirection); void clear_resize_candidate(); ResizeDirection resize_direction_of_window(const WSWindow&); bool any_opaque_window_contains_rect(const Rect&); bool any_opaque_window_above_this_one_contains_rect(const WSWindow&, const Rect&); void tell_wm_listeners_window_state_changed(WSWindow&); void tell_wm_listeners_window_icon_changed(WSWindow&); void tell_wm_listeners_window_rect_changed(WSWindow&); void start_window_resize(WSWindow&, const Point&, MouseButton); void start_window_resize(WSWindow&, const WSMouseEvent&); const WSWindow* active_fullscreen_window() const { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } WSWindow* active_fullscreen_window() { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } void update_theme(String theme_path, String theme_name); private: NonnullRefPtr get_cursor(const String& name); NonnullRefPtr get_cursor(const String& name, const Point& hotspot); void process_mouse_event(WSMouseEvent&, WSWindow*& hovered_window); void process_event_for_doubleclick(WSWindow& window, WSMouseEvent& event); void deliver_mouse_event(WSWindow& window, WSMouseEvent& event); bool process_ongoing_window_resize(const WSMouseEvent&, WSWindow*& hovered_window); bool process_ongoing_window_move(WSMouseEvent&, WSWindow*& hovered_window); bool process_ongoing_drag(WSMouseEvent&, WSWindow*& hovered_window); void start_window_move(WSWindow&, const WSMouseEvent&); void set_hovered_window(WSWindow*); template IterationDecision for_each_visible_window_of_type_from_back_to_front(WSWindowType, Callback, bool ignore_highlight = false); template IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback, bool ignore_highlight = false); template IterationDecision for_each_visible_window_from_front_to_back(Callback); template IterationDecision for_each_visible_window_from_back_to_front(Callback); template void for_each_window_listening_to_wm_events(Callback); template void for_each_window(Callback); template IterationDecision for_each_window_of_type_from_front_to_back(WSWindowType, Callback, bool ignore_highlight = false); virtual void event(CEvent&) override; void paint_window_frame(const WSWindow&); void tell_wm_listener_about_window(WSWindow& listener, WSWindow&); void tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow&); void tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow&); void pick_new_active_window(); void recompute_occlusions(); RefPtr m_arrow_cursor; RefPtr m_hand_cursor; RefPtr m_resize_horizontally_cursor; RefPtr m_resize_vertically_cursor; RefPtr m_resize_diagonally_tlbr_cursor; RefPtr m_resize_diagonally_bltr_cursor; RefPtr m_i_beam_cursor; RefPtr m_disallowed_cursor; RefPtr m_move_cursor; RefPtr m_drag_cursor; Color m_background_color; Color m_active_window_border_color; Color m_active_window_border_color2; Color m_active_window_title_color; Color m_inactive_window_border_color; Color m_inactive_window_border_color2; Color m_inactive_window_title_color; Color m_moving_window_border_color; Color m_moving_window_border_color2; Color m_moving_window_title_color; Color m_highlight_window_border_color; Color m_highlight_window_border_color2; Color m_highlight_window_title_color; InlineLinkedList m_windows_in_order; struct DoubleClickInfo { struct ClickMetadata { CElapsedTimer clock; Point last_position; }; ClickMetadata& metadata_for_button(MouseButton); void reset() { m_left = {}; m_right = {}; m_middle = {}; } WeakPtr m_clicked_window; private: ClickMetadata m_left; ClickMetadata m_right; ClickMetadata m_middle; }; DoubleClickInfo m_double_click_info; int m_double_click_speed { 0 }; int m_max_distance_for_double_click { 4 }; WeakPtr m_active_window; WeakPtr m_hovered_window; WeakPtr m_highlight_window; WeakPtr m_active_input_window; WeakPtr m_move_window; Point m_move_origin; Point m_move_window_origin; WeakPtr m_resize_window; WeakPtr m_resize_candidate; MouseButton m_resizing_mouse_button { MouseButton::None }; Rect m_resize_window_original_rect; Point m_resize_origin; ResizeDirection m_resize_direction { ResizeDirection::None }; bool m_moved_or_resized_since_logo_keydown { false }; u8 m_keyboard_modifiers { 0 }; WSWindowSwitcher m_switcher; WeakPtr m_cursor_tracking_button; WeakPtr m_hovered_button; NonnullRefPtr m_palette; RefPtr m_wm_config; WeakPtr m_dnd_client; String m_dnd_text; String m_dnd_data_type; String m_dnd_data; RefPtr m_dnd_bitmap; }; template IterationDecision WSWindowManager::for_each_visible_window_of_type_from_back_to_front(WSWindowType type, Callback callback, bool ignore_highlight) { bool do_highlight_window_at_end = false; for (auto& window : m_windows_in_order) { if (!window.is_visible()) continue; if (window.is_minimized()) continue; if (window.type() != type) continue; if (!ignore_highlight && m_highlight_window == &window) { do_highlight_window_at_end = true; continue; } if (callback(window) == IterationDecision::Break) return IterationDecision::Break; } if (do_highlight_window_at_end) { if (callback(*m_highlight_window) == IterationDecision::Break) return IterationDecision::Break; } return IterationDecision::Continue; } template IterationDecision WSWindowManager::for_each_visible_window_from_back_to_front(Callback callback) { if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menubar, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Break) return IterationDecision::Break; return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback); } template IterationDecision WSWindowManager::for_each_visible_window_of_type_from_front_to_back(WSWindowType type, Callback callback, bool ignore_highlight) { if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { if (callback(*m_highlight_window) == IterationDecision::Break) return IterationDecision::Break; } for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (!window->is_visible()) continue; if (window->is_minimized()) continue; if (window->type() != type) continue; if (!ignore_highlight && window == m_highlight_window) continue; if (callback(*window) == IterationDecision::Break) return IterationDecision::Break; } return IterationDecision::Continue; } template IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Callback callback) { if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menubar, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Taskbar, callback) == IterationDecision::Break) return IterationDecision::Break; if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, callback) == IterationDecision::Break) return IterationDecision::Break; return for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback); } template void WSWindowManager::for_each_window_listening_to_wm_events(Callback callback) { for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (!window->listens_to_wm_events()) continue; if (callback(*window) == IterationDecision::Break) return; } } template void WSWindowManager::for_each_window(Callback callback) { for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (callback(*window) == IterationDecision::Break) return; } } template IterationDecision WSWindowManager::for_each_window_of_type_from_front_to_back(WSWindowType type, Callback callback, bool ignore_highlight) { if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { if (callback(*m_highlight_window) == IterationDecision::Break) return IterationDecision::Break; } for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { if (window->type() != type) continue; if (!ignore_highlight && window == m_highlight_window) continue; if (callback(*window) == IterationDecision::Break) return IterationDecision::Break; } return IterationDecision::Continue; }