Serenity Operating System
at master 192 lines 5.1 kB view raw
1/* 2 * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> 3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "AppletManager.h" 9#include <AK/QuickSort.h> 10#include <LibCore/EventLoop.h> 11#include <LibGfx/Painter.h> 12#include <WindowServer/MenuManager.h> 13 14namespace WindowServer { 15 16static AppletManager* s_the; 17Vector<DeprecatedString> order_vector; 18 19AppletManager::AppletManager() 20{ 21 s_the = this; 22 23 auto order = g_config->read_entry("Applet", "Order"); 24 order_vector = order.split(','); 25} 26 27AppletManager& AppletManager::the() 28{ 29 VERIFY(s_the); 30 return *s_the; 31} 32 33void AppletManager::set_position(Gfx::IntPoint position) 34{ 35 m_window->move_to(position); 36 m_window->set_visible(true); 37} 38 39void AppletManager::set_hovered_applet(Window* applet) 40{ 41 if (m_hovered_applet == applet) 42 return; 43 44 if (m_hovered_applet) 45 Core::EventLoop::current().post_event(*m_hovered_applet, make<Event>(Event::WindowLeft)); 46 47 m_hovered_applet = applet; 48 49 if (m_hovered_applet) 50 Core::EventLoop::current().post_event(*m_hovered_applet, make<Event>(Event::WindowEntered)); 51} 52 53void AppletManager::event(Core::Event& event) 54{ 55 if (event.type() == Event::WindowLeft && m_hovered_applet) { 56 set_hovered_applet(nullptr); 57 return; 58 } 59 60 if (!is<MouseEvent>(event)) 61 return; 62 auto& mouse_event = static_cast<MouseEvent&>(event); 63 64 for (auto& applet : m_applets) { 65 if (!applet) 66 continue; 67 if (!applet->rect_in_applet_area().contains(mouse_event.position())) 68 continue; 69 auto local_event = mouse_event.translated(-applet->rect_in_applet_area().location()); 70 set_hovered_applet(applet); 71 Core::EventLoop::current().post_event(*applet, make<MouseEvent>(local_event)); 72 return; 73 } 74} 75 76void AppletManager::add_applet(Window& applet) 77{ 78 m_applets.append(applet); 79 80 // Prune any dead weak pointers from the applet list. 81 m_applets.remove_all_matching([](auto& entry) { 82 return entry.is_null(); 83 }); 84 85 quick_sort(m_applets, [](auto& a, auto& b) { 86 auto index_a = order_vector.find_first_index(a->title()); 87 auto index_b = order_vector.find_first_index(b->title()); 88 return index_a.value_or(0) > index_b.value_or(0); 89 }); 90 91 relayout(); 92} 93 94void AppletManager::relayout() 95{ 96 constexpr int applet_spacing = 4; 97 constexpr int applet_window_height = 19; 98 int total_width = 0; 99 for (auto& existing_applet : m_applets) { 100 total_width += max(0, existing_applet->size().width()) + applet_spacing; 101 } 102 103 if (total_width > 0) 104 total_width -= applet_spacing; 105 106 auto right_edge_x = total_width; 107 108 for (auto& existing_applet : m_applets) { 109 auto applet_size = existing_applet->size(); 110 Gfx::IntRect new_applet_rect(right_edge_x - applet_size.width(), 0, applet_size.width(), applet_size.height()); 111 112 Gfx::IntRect dummy_container_rect(0, 0, 0, applet_window_height); 113 new_applet_rect.center_vertically_within(dummy_container_rect); 114 115 existing_applet->set_rect_in_applet_area(new_applet_rect); 116 right_edge_x = existing_applet->rect_in_applet_area().x() - applet_spacing; 117 } 118 119 if (!m_window) { 120 m_window = Window::construct(*this, WindowType::AppletArea); 121 m_window->set_visible(false); 122 } 123 124 Gfx::IntRect rect { m_window->position(), Gfx::IntSize { total_width, applet_window_height } }; 125 if (m_window->rect() == rect) 126 return; 127 m_window->set_rect(rect); 128 129 repaint(); 130 131 WindowManager::the().tell_wms_applet_area_size_changed(rect.size()); 132} 133 134void AppletManager::repaint() 135{ 136 if (!m_window) { 137 return; 138 } 139 140 auto rect = Gfx::IntRect { { 0, 0 }, m_window->size() }; 141 142 if (!rect.is_empty()) { 143 Gfx::Painter painter(*m_window->backing_store()); 144 painter.fill_rect(rect, WindowManager::the().palette().button()); 145 } 146} 147 148void AppletManager::did_change_theme() 149{ 150 repaint(); 151} 152 153void AppletManager::remove_applet(Window& applet) 154{ 155 m_applets.remove_first_matching([&](auto& entry) { 156 return &applet == entry.ptr(); 157 }); 158 159 relayout(); 160} 161 162void AppletManager::draw() 163{ 164 for (auto& applet : m_applets) { 165 if (!applet) 166 continue; 167 draw_applet(*applet); 168 } 169} 170 171void AppletManager::draw_applet(Window const& applet) 172{ 173 if (!applet.backing_store()) 174 return; 175 176 Gfx::Painter painter(*m_window->backing_store()); 177 Gfx::PainterStateSaver saver(painter); 178 painter.add_clip_rect(applet.rect_in_applet_area()); 179 painter.fill_rect(applet.rect_in_applet_area(), WindowManager::the().palette().button()); 180 painter.blit(applet.rect_in_applet_area().location(), *applet.backing_store(), applet.backing_store()->rect()); 181} 182 183void AppletManager::invalidate_applet(Window const& applet, Gfx::IntRect const&) 184{ 185 draw_applet(applet); 186 draw(); 187 // FIXME: Invalidate only the exact rect we've been given. 188 if (m_window) 189 m_window->invalidate(); 190} 191 192}