Serenity Operating System
at master 162 lines 5.5 kB view raw
1/* 2 * Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * Copyright (c) 2022, networkException <networkexception@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include "DesktopStatusWindow.h" 10#include <LibCore/Process.h> 11#include <LibGUI/ConnectionToWindowManagerServer.h> 12#include <LibGUI/Desktop.h> 13#include <LibGUI/Menu.h> 14#include <LibGUI/Painter.h> 15#include <LibGUI/Widget.h> 16#include <LibGfx/Palette.h> 17 18class DesktopStatusWidget : public GUI::Widget { 19 C_OBJECT(DesktopStatusWidget); 20 21public: 22 virtual ~DesktopStatusWidget() override = default; 23 24 Gfx::IntRect rect_for_desktop(unsigned row, unsigned column) const 25 { 26 auto& desktop = GUI::Desktop::the(); 27 28 auto workspace_columns = desktop.workspace_columns(); 29 auto workspace_rows = desktop.workspace_rows(); 30 31 auto desktop_width = (width() - gap() * (workspace_columns - 1)) / workspace_columns; 32 auto desktop_height = (height() - gap() * (workspace_rows - 1)) / workspace_rows; 33 34 return { 35 column * (desktop_width + gap()), row * (desktop_height + gap()), 36 desktop_width, desktop_height 37 }; 38 } 39 40 virtual void paint_event(GUI::PaintEvent& event) override 41 { 42 GUI::Widget::paint_event(event); 43 44 GUI::Painter painter(*this); 45 painter.add_clip_rect(event.rect()); 46 painter.fill_rect({ 0, 0, width(), height() }, palette().button()); 47 48 auto& desktop = GUI::Desktop::the(); 49 50 auto active_color = palette().selection(); 51 auto inactive_color = palette().window().darkened(0.9f); 52 53 for (unsigned row = 0; row < desktop.workspace_rows(); ++row) { 54 for (unsigned column = 0; column < desktop.workspace_columns(); ++column) { 55 auto rect = rect_for_desktop(row, column); 56 painter.fill_rect(rect, 57 (row == current_row() && column == current_column()) ? active_color : inactive_color); 58 Gfx::StylePainter::current().paint_frame(painter, rect, palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 1); 59 } 60 } 61 } 62 63 virtual void mousedown_event(GUI::MouseEvent& event) override 64 { 65 if (event.button() != GUI::MouseButton::Primary) 66 return; 67 68 auto base_rect = rect_for_desktop(0, 0); 69 auto row = event.y() / (base_rect.height() + gap()); 70 auto column = event.x() / (base_rect.width() + gap()); 71 72 // Handle case where divider is clicked. 73 if (rect_for_desktop(row, column).contains(event.position())) { 74 GUI::ConnectionToWindowManagerServer::the().async_set_workspace(row, column); 75 76 set_current_row(row); 77 set_current_column(column); 78 update(); 79 } 80 } 81 82 virtual void mousewheel_event(GUI::MouseEvent& event) override 83 { 84 auto& desktop = GUI::Desktop::the(); 85 86 auto column = current_column(); 87 auto row = current_row(); 88 89 auto workspace_columns = desktop.workspace_columns(); 90 auto workspace_rows = desktop.workspace_rows(); 91 auto direction = event.wheel_delta_y() < 0 ? 1 : -1; 92 93 if (event.modifiers() & Mod_Shift) 94 column = abs((int)column + direction) % workspace_columns; 95 else 96 row = abs((int)row + direction) % workspace_rows; 97 98 set_current_row(row); 99 set_current_column(column); 100 update(); 101 102 GUI::ConnectionToWindowManagerServer::the().async_set_workspace(row, column); 103 } 104 105 virtual void context_menu_event(GUI::ContextMenuEvent& event) override 106 { 107 event.accept(); 108 109 if (!m_context_menu) { 110 m_context_menu = GUI::Menu::construct(); 111 112 auto settings_icon = MUST(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)); 113 auto open_workspace_settings_action = GUI::Action::create("Workspace &Settings", *settings_icon, [](auto&) { 114 auto result = Core::Process::spawn("/bin/DisplaySettings"sv, Array { "--open-tab", "workspaces" }.span()); 115 if (result.is_error()) { 116 dbgln("Failed to launch DisplaySettings"); 117 } 118 }); 119 m_context_menu->add_action(open_workspace_settings_action); 120 } 121 122 m_context_menu->popup(event.screen_position()); 123 } 124 125 unsigned 126 current_row() const 127 { 128 return m_current_row; 129 } 130 void set_current_row(unsigned row) { m_current_row = row; } 131 unsigned current_column() const { return m_current_column; } 132 void set_current_column(unsigned column) { m_current_column = column; } 133 134 unsigned gap() const { return m_gap; } 135 136private: 137 DesktopStatusWidget() = default; 138 139 unsigned m_gap { 1 }; 140 141 unsigned m_current_row { 0 }; 142 unsigned m_current_column { 0 }; 143 144 RefPtr<GUI::Menu> m_context_menu; 145}; 146 147DesktopStatusWindow::DesktopStatusWindow() 148{ 149 set_window_type(GUI::WindowType::Applet); 150 set_has_alpha_channel(true); 151 m_widget = set_main_widget<DesktopStatusWidget>().release_value_but_fixme_should_propagate_errors(); 152} 153 154void DesktopStatusWindow::wm_event(GUI::WMEvent& event) 155{ 156 if (event.type() == GUI::Event::WM_WorkspaceChanged) { 157 auto& changed_event = static_cast<GUI::WMWorkspaceChangedEvent&>(event); 158 m_widget->set_current_row(changed_event.current_row()); 159 m_widget->set_current_column(changed_event.current_column()); 160 update(); 161 } 162}