Window.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "Window.h"
  27. #include "AppletManager.h"
  28. #include "ClientConnection.h"
  29. #include "Compositor.h"
  30. #include "Event.h"
  31. #include "EventLoop.h"
  32. #include "Screen.h"
  33. #include "WindowManager.h"
  34. #include <AK/Badge.h>
  35. #include <WindowServer/WindowClientEndpoint.h>
  36. namespace WindowServer {
  37. const static Gfx::IntSize s_default_normal_minimum_size = { 50, 50 };
  38. static String default_window_icon_path()
  39. {
  40. return "/res/icons/16x16/window.png";
  41. }
  42. static Gfx::Bitmap& default_window_icon()
  43. {
  44. static Gfx::Bitmap* s_icon;
  45. if (!s_icon)
  46. s_icon = Gfx::Bitmap::load_from_file(default_window_icon_path()).leak_ref();
  47. return *s_icon;
  48. }
  49. static Gfx::Bitmap& minimize_icon()
  50. {
  51. static Gfx::Bitmap* s_icon;
  52. if (!s_icon)
  53. s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png").leak_ref();
  54. return *s_icon;
  55. }
  56. static Gfx::Bitmap& maximize_icon()
  57. {
  58. static Gfx::Bitmap* s_icon;
  59. if (!s_icon)
  60. s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png").leak_ref();
  61. return *s_icon;
  62. }
  63. static Gfx::Bitmap& restore_icon()
  64. {
  65. static Gfx::Bitmap* s_icon;
  66. if (!s_icon)
  67. s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png").leak_ref();
  68. return *s_icon;
  69. }
  70. static Gfx::Bitmap& close_icon()
  71. {
  72. static Gfx::Bitmap* s_icon;
  73. if (!s_icon)
  74. s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png").leak_ref();
  75. return *s_icon;
  76. }
  77. Window::Window(Core::Object& parent, WindowType type)
  78. : Core::Object(&parent)
  79. , m_type(type)
  80. , m_icon(default_window_icon())
  81. , m_frame(*this)
  82. {
  83. // Set default minimum size for Normal windows
  84. if (m_type == WindowType::Normal)
  85. m_minimum_size = s_default_normal_minimum_size;
  86. WindowManager::the().add_window(*this);
  87. }
  88. Window::Window(ClientConnection& client, WindowType window_type, int window_id, bool modal, bool minimizable, bool frameless, bool resizable, bool fullscreen, bool accessory, Window* parent_window)
  89. : Core::Object(&client)
  90. , m_client(&client)
  91. , m_type(window_type)
  92. , m_modal(modal)
  93. , m_minimizable(minimizable)
  94. , m_frameless(frameless)
  95. , m_resizable(resizable)
  96. , m_fullscreen(fullscreen)
  97. , m_accessory(accessory)
  98. , m_window_id(window_id)
  99. , m_client_id(client.client_id())
  100. , m_icon(default_window_icon())
  101. , m_frame(*this)
  102. {
  103. // FIXME: This should not be hard-coded here.
  104. if (m_type == WindowType::Taskbar) {
  105. m_wm_event_mask = WMEventMask::WindowStateChanges | WMEventMask::WindowRemovals | WMEventMask::WindowIconChanges;
  106. m_listens_to_wm_events = true;
  107. }
  108. // Set default minimum size for Normal windows
  109. if (m_type == WindowType::Normal)
  110. m_minimum_size = s_default_normal_minimum_size;
  111. if (parent_window)
  112. set_parent_window(*parent_window);
  113. WindowManager::the().add_window(*this);
  114. }
  115. Window::~Window()
  116. {
  117. // Detach from client at the start of teardown since we don't want
  118. // to confuse things by trying to send messages to it.
  119. m_client = nullptr;
  120. WindowManager::the().remove_window(*this);
  121. }
  122. void Window::destroy()
  123. {
  124. m_destroyed = true;
  125. set_visible(false);
  126. }
  127. void Window::set_title(const String& title)
  128. {
  129. if (m_title == title)
  130. return;
  131. m_title = title;
  132. frame().invalidate_title_bar();
  133. WindowManager::the().notify_title_changed(*this);
  134. }
  135. void Window::set_rect(const Gfx::IntRect& rect)
  136. {
  137. VERIFY(!rect.is_empty());
  138. if (m_rect == rect)
  139. return;
  140. auto old_rect = m_rect;
  141. m_rect = rect;
  142. if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
  143. m_backing_store = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, m_rect.size());
  144. }
  145. invalidate(true, old_rect.size() != rect.size());
  146. m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions
  147. }
  148. void Window::set_rect_without_repaint(const Gfx::IntRect& rect)
  149. {
  150. VERIFY(!rect.is_empty());
  151. if (m_rect == rect)
  152. return;
  153. auto old_rect = m_rect;
  154. m_rect = rect;
  155. if (old_rect.size() == m_rect.size()) {
  156. auto delta = m_rect.location() - old_rect.location();
  157. for (auto& child_window : m_child_windows) {
  158. if (child_window)
  159. child_window->move_by(delta);
  160. }
  161. }
  162. invalidate(true, old_rect.size() != rect.size());
  163. m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions
  164. }
  165. bool Window::apply_minimum_size(Gfx::IntRect& rect)
  166. {
  167. int new_width = max(m_minimum_size.width(), rect.width());
  168. int new_height = max(m_minimum_size.height(), rect.height());
  169. bool did_size_clamp = new_width != rect.width() || new_height != rect.height();
  170. rect.set_width(new_width);
  171. rect.set_height(new_height);
  172. return did_size_clamp;
  173. }
  174. void Window::nudge_into_desktop(bool force_titlebar_visible)
  175. {
  176. Gfx::IntRect arena = WindowManager::the().arena_rect_for_type(type());
  177. auto min_visible = 1;
  178. if (type() == WindowType::Normal)
  179. min_visible = 30;
  180. // Push the frame around such that at least `min_visible` pixels of the *frame* are in the desktop rect.
  181. auto old_frame_rect = frame().rect();
  182. Gfx::IntRect new_frame_rect = {
  183. clamp(old_frame_rect.x(), arena.left() + min_visible - width(), arena.right() - min_visible),
  184. clamp(old_frame_rect.y(), arena.top() + min_visible - height(), arena.bottom() - min_visible),
  185. old_frame_rect.width(),
  186. old_frame_rect.height(),
  187. };
  188. // Make sure that at least half of the titlebar is visible.
  189. auto min_frame_y = arena.top() - (y() - old_frame_rect.y()) / 2;
  190. if (force_titlebar_visible && new_frame_rect.y() < min_frame_y) {
  191. new_frame_rect.set_y(min_frame_y);
  192. }
  193. // Deduce new window rect:
  194. Gfx::IntRect new_window_rect = {
  195. x() + new_frame_rect.x() - old_frame_rect.x(),
  196. y() + new_frame_rect.y() - old_frame_rect.y(),
  197. width(),
  198. height(),
  199. };
  200. set_rect(new_window_rect);
  201. }
  202. void Window::set_minimum_size(const Gfx::IntSize& size)
  203. {
  204. VERIFY(!size.is_empty());
  205. if (m_minimum_size == size)
  206. return;
  207. // Disallow setting minimum zero widths or heights.
  208. if (size.width() == 0 || size.height() == 0)
  209. return;
  210. m_minimum_size = size;
  211. }
  212. void Window::handle_mouse_event(const MouseEvent& event)
  213. {
  214. set_automatic_cursor_tracking_enabled(event.buttons() != 0);
  215. switch (event.type()) {
  216. case Event::MouseMove:
  217. m_client->post_message(Messages::WindowClient::MouseMove(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta(), event.is_drag(), event.mime_types()));
  218. break;
  219. case Event::MouseDown:
  220. m_client->post_message(Messages::WindowClient::MouseDown(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta()));
  221. break;
  222. case Event::MouseDoubleClick:
  223. m_client->post_message(Messages::WindowClient::MouseDoubleClick(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta()));
  224. break;
  225. case Event::MouseUp:
  226. m_client->post_message(Messages::WindowClient::MouseUp(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta()));
  227. break;
  228. case Event::MouseWheel:
  229. m_client->post_message(Messages::WindowClient::MouseWheel(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta()));
  230. break;
  231. default:
  232. VERIFY_NOT_REACHED();
  233. }
  234. }
  235. void Window::update_menu_item_text(PopupMenuItem item)
  236. {
  237. if (m_window_menu) {
  238. m_window_menu->item((int)item).set_text(item == PopupMenuItem::Minimize ? (m_minimized ? "Unminimize" : "Minimize") : (m_maximized ? "Restore" : "Maximize"));
  239. m_window_menu->redraw();
  240. }
  241. }
  242. void Window::update_menu_item_enabled(PopupMenuItem item)
  243. {
  244. if (m_window_menu) {
  245. m_window_menu->item((int)item).set_enabled(item == PopupMenuItem::Minimize ? m_minimizable : m_resizable);
  246. m_window_menu->redraw();
  247. }
  248. }
  249. void Window::set_minimized(bool minimized)
  250. {
  251. if (m_minimized == minimized)
  252. return;
  253. if (minimized && !m_minimizable)
  254. return;
  255. m_minimized = minimized;
  256. update_menu_item_text(PopupMenuItem::Minimize);
  257. Compositor::the().invalidate_occlusions();
  258. Compositor::the().invalidate_screen(frame().render_rect());
  259. if (!blocking_modal_window())
  260. start_minimize_animation();
  261. if (!minimized)
  262. request_update({ {}, size() });
  263. WindowManager::the().notify_minimization_state_changed(*this);
  264. }
  265. void Window::set_minimizable(bool minimizable)
  266. {
  267. if (m_minimizable == minimizable)
  268. return;
  269. m_minimizable = minimizable;
  270. update_menu_item_enabled(PopupMenuItem::Minimize);
  271. // TODO: Hide/show (or alternatively change enabled state of) window minimize button dynamically depending on value of m_minimizable
  272. }
  273. void Window::set_taskbar_rect(const Gfx::IntRect& rect)
  274. {
  275. m_taskbar_rect = rect;
  276. m_have_taskbar_rect = !m_taskbar_rect.is_empty();
  277. }
  278. void Window::start_minimize_animation()
  279. {
  280. if (!m_have_taskbar_rect) {
  281. // If this is a modal window, it may not have its own taskbar
  282. // button, so there is no rectangle. In that case, walk the
  283. // modal stack until we find a window that may have one
  284. WindowManager::the().for_each_window_in_modal_stack(*this, [&](Window& w, bool) {
  285. if (w.has_taskbar_rect()) {
  286. // We purposely do NOT set m_have_taskbar_rect to true here
  287. // because we want to only copy the rectangle from the
  288. // window that has it, but since this window wouldn't receive
  289. // any updates down the road we want to query it again
  290. // next time we want to start the animation
  291. m_taskbar_rect = w.taskbar_rect();
  292. VERIFY(!m_have_taskbar_rect); // should remain unset!
  293. return IterationDecision::Break;
  294. };
  295. return IterationDecision::Continue;
  296. });
  297. }
  298. m_minimize_animation_step = 0;
  299. }
  300. void Window::set_opacity(float opacity)
  301. {
  302. if (m_opacity == opacity)
  303. return;
  304. bool was_opaque = is_opaque();
  305. m_opacity = opacity;
  306. if (was_opaque != is_opaque())
  307. Compositor::the().invalidate_occlusions();
  308. invalidate(false);
  309. WindowManager::the().notify_opacity_changed(*this);
  310. }
  311. void Window::set_has_alpha_channel(bool value)
  312. {
  313. if (m_has_alpha_channel == value)
  314. return;
  315. m_has_alpha_channel = value;
  316. Compositor::the().invalidate_occlusions();
  317. }
  318. void Window::set_occluded(bool occluded)
  319. {
  320. if (m_occluded == occluded)
  321. return;
  322. m_occluded = occluded;
  323. WindowManager::the().notify_occlusion_state_changed(*this);
  324. }
  325. void Window::set_maximized(bool maximized, Optional<Gfx::IntPoint> fixed_point)
  326. {
  327. if (m_maximized == maximized)
  328. return;
  329. if (maximized && (!is_resizable() || resize_aspect_ratio().has_value()))
  330. return;
  331. m_tiled = WindowTileType::None;
  332. m_maximized = maximized;
  333. update_menu_item_text(PopupMenuItem::Maximize);
  334. if (maximized) {
  335. m_unmaximized_rect = m_rect;
  336. set_rect(WindowManager::the().maximized_window_rect(*this));
  337. } else {
  338. if (fixed_point.has_value()) {
  339. auto new_rect = Gfx::IntRect(m_rect);
  340. new_rect.set_size_around(m_unmaximized_rect.size(), fixed_point.value());
  341. set_rect(new_rect);
  342. } else {
  343. set_rect(m_unmaximized_rect);
  344. }
  345. }
  346. m_frame.did_set_maximized({}, maximized);
  347. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
  348. set_default_positioned(false);
  349. }
  350. void Window::set_vertically_maximized()
  351. {
  352. if (m_maximized)
  353. return;
  354. if (!is_resizable() || resize_aspect_ratio().has_value())
  355. return;
  356. auto max_rect = WindowManager::the().maximized_window_rect(*this);
  357. auto new_rect = Gfx::IntRect(
  358. Gfx::IntPoint(rect().x(), max_rect.y()),
  359. Gfx::IntSize(rect().width(), max_rect.height()));
  360. set_rect(new_rect);
  361. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(new_rect));
  362. }
  363. void Window::set_resizable(bool resizable)
  364. {
  365. if (m_resizable == resizable)
  366. return;
  367. m_resizable = resizable;
  368. update_menu_item_enabled(PopupMenuItem::Maximize);
  369. // TODO: Hide/show (or alternatively change enabled state of) window maximize button dynamically depending on value of is_resizable()
  370. }
  371. void Window::event(Core::Event& event)
  372. {
  373. if (!m_client) {
  374. VERIFY(parent());
  375. event.ignore();
  376. return;
  377. }
  378. if (blocking_modal_window()) {
  379. // We still want to handle the WindowDeactivated event below when a new modal is
  380. // created to notify its parent window, despite it being "blocked by modal window".
  381. if (event.type() != Event::WindowDeactivated)
  382. return;
  383. }
  384. if (static_cast<Event&>(event).is_mouse_event())
  385. return handle_mouse_event(static_cast<const MouseEvent&>(event));
  386. switch (event.type()) {
  387. case Event::WindowEntered:
  388. m_client->post_message(Messages::WindowClient::WindowEntered(m_window_id));
  389. break;
  390. case Event::WindowLeft:
  391. m_client->post_message(Messages::WindowClient::WindowLeft(m_window_id));
  392. break;
  393. case Event::KeyDown:
  394. m_client->post_message(
  395. Messages::WindowClient::KeyDown(m_window_id,
  396. (u32) static_cast<const KeyEvent&>(event).code_point(),
  397. (u32) static_cast<const KeyEvent&>(event).key(),
  398. static_cast<const KeyEvent&>(event).modifiers(),
  399. (u32) static_cast<const KeyEvent&>(event).scancode()));
  400. break;
  401. case Event::KeyUp:
  402. m_client->post_message(
  403. Messages::WindowClient::KeyUp(m_window_id,
  404. (u32) static_cast<const KeyEvent&>(event).code_point(),
  405. (u32) static_cast<const KeyEvent&>(event).key(),
  406. static_cast<const KeyEvent&>(event).modifiers(),
  407. (u32) static_cast<const KeyEvent&>(event).scancode()));
  408. break;
  409. case Event::WindowActivated:
  410. m_client->post_message(Messages::WindowClient::WindowActivated(m_window_id));
  411. break;
  412. case Event::WindowDeactivated:
  413. m_client->post_message(Messages::WindowClient::WindowDeactivated(m_window_id));
  414. break;
  415. case Event::WindowInputEntered:
  416. m_client->post_message(Messages::WindowClient::WindowInputEntered(m_window_id));
  417. break;
  418. case Event::WindowInputLeft:
  419. m_client->post_message(Messages::WindowClient::WindowInputLeft(m_window_id));
  420. break;
  421. case Event::WindowCloseRequest:
  422. m_client->post_message(Messages::WindowClient::WindowCloseRequest(m_window_id));
  423. break;
  424. case Event::WindowResized:
  425. m_client->post_message(Messages::WindowClient::WindowResized(m_window_id, static_cast<const ResizeEvent&>(event).rect()));
  426. break;
  427. default:
  428. break;
  429. }
  430. }
  431. void Window::set_global_cursor_tracking_enabled(bool enabled)
  432. {
  433. m_global_cursor_tracking_enabled = enabled;
  434. }
  435. void Window::set_visible(bool b)
  436. {
  437. if (m_visible == b)
  438. return;
  439. m_visible = b;
  440. Compositor::the().invalidate_occlusions();
  441. if (m_visible)
  442. invalidate(true);
  443. else
  444. Compositor::the().invalidate_screen(frame().render_rect());
  445. }
  446. void Window::set_frameless(bool frameless)
  447. {
  448. if (m_frameless == frameless)
  449. return;
  450. auto render_rect_before = frame().render_rect();
  451. m_frameless = frameless;
  452. if (m_visible) {
  453. Compositor::the().invalidate_occlusions();
  454. invalidate(true, true);
  455. Compositor::the().invalidate_screen(frameless ? render_rect_before : frame().render_rect());
  456. }
  457. }
  458. void Window::invalidate(bool invalidate_frame, bool re_render_frame)
  459. {
  460. m_invalidated = true;
  461. m_invalidated_all = true;
  462. if (invalidate_frame && !m_invalidated_frame) {
  463. m_invalidated_frame = true;
  464. }
  465. if (re_render_frame)
  466. frame().set_dirty(true);
  467. m_dirty_rects.clear();
  468. Compositor::the().invalidate_window();
  469. }
  470. void Window::invalidate(const Gfx::IntRect& rect, bool with_frame)
  471. {
  472. if (type() == WindowType::MenuApplet) {
  473. AppletManager::the().invalidate_applet(*this, rect);
  474. return;
  475. }
  476. if (invalidate_no_notify(rect, with_frame))
  477. Compositor::the().invalidate_window();
  478. }
  479. bool Window::invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame)
  480. {
  481. if (rect.is_empty())
  482. return false;
  483. if (m_invalidated_all) {
  484. if (with_frame)
  485. m_invalidated_frame |= true;
  486. return false;
  487. }
  488. auto outer_rect = frame().render_rect();
  489. auto inner_rect = rect;
  490. inner_rect.move_by(position());
  491. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
  492. inner_rect.intersect(outer_rect);
  493. if (inner_rect.is_empty())
  494. return false;
  495. m_invalidated = true;
  496. if (with_frame)
  497. m_invalidated_frame |= true;
  498. m_dirty_rects.add(inner_rect.translated(-outer_rect.location()));
  499. return true;
  500. }
  501. void Window::refresh_client_size()
  502. {
  503. client()->post_message(Messages::WindowClient::WindowResized(m_window_id, m_rect));
  504. }
  505. void Window::prepare_dirty_rects()
  506. {
  507. if (m_invalidated_all) {
  508. if (m_invalidated_frame)
  509. m_dirty_rects = frame().render_rect();
  510. else
  511. m_dirty_rects = rect();
  512. } else {
  513. m_dirty_rects.move_by(frame().render_rect().location());
  514. if (m_invalidated_frame) {
  515. if (m_invalidated) {
  516. m_dirty_rects.add(frame().render_rect());
  517. } else {
  518. for (auto& rects : frame().render_rect().shatter(rect()))
  519. m_dirty_rects.add(rects);
  520. }
  521. }
  522. }
  523. }
  524. void Window::clear_dirty_rects()
  525. {
  526. m_invalidated_all = false;
  527. m_invalidated_frame = false;
  528. m_invalidated = false;
  529. m_dirty_rects.clear_with_capacity();
  530. }
  531. bool Window::is_active() const
  532. {
  533. return WindowManager::the().active_window() == this;
  534. }
  535. Window* Window::blocking_modal_window()
  536. {
  537. // A window is blocked if any immediate child, or any child further
  538. // down the chain is modal
  539. for (auto& window : m_child_windows) {
  540. if (window && !window->is_destroyed()) {
  541. if (window->is_modal())
  542. return window;
  543. if (auto* blocking_window = window->blocking_modal_window())
  544. return blocking_window;
  545. }
  546. }
  547. return nullptr;
  548. }
  549. void Window::set_default_icon()
  550. {
  551. m_icon = default_window_icon();
  552. }
  553. void Window::request_update(const Gfx::IntRect& rect, bool ignore_occlusion)
  554. {
  555. if (rect.is_empty())
  556. return;
  557. if (m_pending_paint_rects.is_empty()) {
  558. deferred_invoke([this, ignore_occlusion](auto&) {
  559. client()->post_paint_message(*this, ignore_occlusion);
  560. });
  561. }
  562. m_pending_paint_rects.add(rect);
  563. }
  564. void Window::ensure_window_menu()
  565. {
  566. if (!m_window_menu) {
  567. m_window_menu = Menu::construct(nullptr, -1, "(Window Menu)");
  568. m_window_menu->set_window_menu_of(*this);
  569. auto minimize_item = make<MenuItem>(*m_window_menu, 1, m_minimized ? "Unminimize" : "Minimize");
  570. m_window_menu_minimize_item = minimize_item.ptr();
  571. m_window_menu->add_item(move(minimize_item));
  572. auto maximize_item = make<MenuItem>(*m_window_menu, 2, m_maximized ? "Restore" : "Maximize");
  573. m_window_menu_maximize_item = maximize_item.ptr();
  574. m_window_menu->add_item(move(maximize_item));
  575. m_window_menu->add_item(make<MenuItem>(*m_window_menu, MenuItem::Type::Separator));
  576. auto close_item = make<MenuItem>(*m_window_menu, 3, "Close");
  577. m_window_menu_close_item = close_item.ptr();
  578. m_window_menu_close_item->set_icon(&close_icon());
  579. m_window_menu_close_item->set_default(true);
  580. m_window_menu->add_item(move(close_item));
  581. m_window_menu->item((int)PopupMenuItem::Minimize).set_enabled(m_minimizable);
  582. m_window_menu->item((int)PopupMenuItem::Maximize).set_enabled(m_resizable);
  583. m_window_menu->on_item_activation = [&](auto& item) {
  584. switch (item.identifier()) {
  585. case 1:
  586. WindowManager::the().minimize_windows(*this, !m_minimized);
  587. if (!m_minimized)
  588. WindowManager::the().move_to_front_and_make_active(*this);
  589. break;
  590. case 2:
  591. WindowManager::the().maximize_windows(*this, !m_maximized);
  592. WindowManager::the().move_to_front_and_make_active(*this);
  593. break;
  594. case 3:
  595. request_close();
  596. break;
  597. }
  598. };
  599. }
  600. }
  601. void Window::popup_window_menu(const Gfx::IntPoint& position, WindowMenuDefaultAction default_action)
  602. {
  603. ensure_window_menu();
  604. if (default_action == WindowMenuDefaultAction::BasedOnWindowState) {
  605. // When clicked on the task bar, determine the default action
  606. if (!is_active() && !is_minimized())
  607. default_action = WindowMenuDefaultAction::None;
  608. else if (is_minimized())
  609. default_action = WindowMenuDefaultAction::Unminimize;
  610. else
  611. default_action = WindowMenuDefaultAction::Minimize;
  612. }
  613. m_window_menu_minimize_item->set_default(default_action == WindowMenuDefaultAction::Minimize || default_action == WindowMenuDefaultAction::Unminimize);
  614. m_window_menu_minimize_item->set_icon(m_minimized ? nullptr : &minimize_icon());
  615. m_window_menu_maximize_item->set_default(default_action == WindowMenuDefaultAction::Maximize || default_action == WindowMenuDefaultAction::Restore);
  616. m_window_menu_maximize_item->set_icon(m_maximized ? &restore_icon() : &maximize_icon());
  617. m_window_menu_close_item->set_default(default_action == WindowMenuDefaultAction::Close);
  618. m_window_menu->popup(position);
  619. }
  620. void Window::window_menu_activate_default()
  621. {
  622. ensure_window_menu();
  623. m_window_menu->activate_default();
  624. }
  625. void Window::request_close()
  626. {
  627. Event close_request(Event::WindowCloseRequest);
  628. event(close_request);
  629. }
  630. void Window::set_fullscreen(bool fullscreen)
  631. {
  632. if (m_fullscreen == fullscreen)
  633. return;
  634. m_fullscreen = fullscreen;
  635. Gfx::IntRect new_window_rect = m_rect;
  636. if (m_fullscreen) {
  637. m_saved_nonfullscreen_rect = m_rect;
  638. new_window_rect = Screen::the().rect();
  639. } else if (!m_saved_nonfullscreen_rect.is_empty()) {
  640. new_window_rect = m_saved_nonfullscreen_rect;
  641. }
  642. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(new_window_rect));
  643. set_rect(new_window_rect);
  644. }
  645. Gfx::IntRect Window::tiled_rect(WindowTileType tiled) const
  646. {
  647. VERIFY(tiled != WindowTileType::None);
  648. int frame_width = (m_frame.rect().width() - m_rect.width()) / 2;
  649. int title_bar_height = m_frame.title_bar_rect().height();
  650. int menu_height = WindowManager::the().maximized_window_rect(*this).y();
  651. int max_height = WindowManager::the().maximized_window_rect(*this).height();
  652. switch (tiled) {
  653. case WindowTileType::Left:
  654. return Gfx::IntRect(0,
  655. menu_height,
  656. Screen::the().width() / 2 - frame_width,
  657. max_height);
  658. case WindowTileType::Right:
  659. return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
  660. menu_height,
  661. Screen::the().width() / 2 - frame_width,
  662. max_height);
  663. case WindowTileType::Top:
  664. return Gfx::IntRect(0,
  665. menu_height,
  666. Screen::the().width() - frame_width,
  667. (max_height - title_bar_height) / 2 - frame_width);
  668. case WindowTileType::Bottom:
  669. return Gfx::IntRect(0,
  670. menu_height + (title_bar_height + max_height) / 2 + frame_width,
  671. Screen::the().width() - frame_width,
  672. (max_height - title_bar_height) / 2 - frame_width);
  673. case WindowTileType::TopLeft:
  674. return Gfx::IntRect(0,
  675. menu_height,
  676. Screen::the().width() / 2 - frame_width,
  677. (max_height - title_bar_height) / 2 - frame_width);
  678. case WindowTileType::TopRight:
  679. return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
  680. menu_height,
  681. Screen::the().width() / 2 - frame_width,
  682. (max_height - title_bar_height) / 2 - frame_width);
  683. case WindowTileType::BottomLeft:
  684. return Gfx::IntRect(0,
  685. menu_height + (title_bar_height + max_height) / 2 + frame_width,
  686. Screen::the().width() / 2 - frame_width,
  687. (max_height - title_bar_height) / 2);
  688. case WindowTileType::BottomRight:
  689. return Gfx::IntRect(Screen::the().width() / 2 + frame_width,
  690. menu_height + (title_bar_height + max_height) / 2 + frame_width,
  691. Screen::the().width() / 2 - frame_width,
  692. (max_height - title_bar_height) / 2);
  693. default:
  694. VERIFY_NOT_REACHED();
  695. }
  696. }
  697. bool Window::set_untiled(Optional<Gfx::IntPoint> fixed_point)
  698. {
  699. if (m_tiled == WindowTileType::None)
  700. return false;
  701. VERIFY(!resize_aspect_ratio().has_value());
  702. m_tiled = WindowTileType::None;
  703. if (fixed_point.has_value()) {
  704. auto new_rect = Gfx::IntRect(m_rect);
  705. new_rect.set_size_around(m_untiled_rect.size(), fixed_point.value());
  706. set_rect(new_rect);
  707. } else {
  708. set_rect(m_untiled_rect);
  709. }
  710. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
  711. return true;
  712. }
  713. void Window::set_tiled(WindowTileType tiled)
  714. {
  715. VERIFY(tiled != WindowTileType::None);
  716. if (m_tiled == tiled)
  717. return;
  718. if (resize_aspect_ratio().has_value())
  719. return;
  720. if (m_tiled == WindowTileType::None)
  721. m_untiled_rect = m_rect;
  722. m_tiled = tiled;
  723. set_rect(tiled_rect(tiled));
  724. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
  725. }
  726. void Window::detach_client(Badge<ClientConnection>)
  727. {
  728. m_client = nullptr;
  729. }
  730. void Window::recalculate_rect()
  731. {
  732. if (!is_resizable())
  733. return;
  734. bool send_event = true;
  735. if (m_tiled != WindowTileType::None) {
  736. set_rect(tiled_rect(m_tiled));
  737. } else if (is_maximized()) {
  738. set_rect(WindowManager::the().maximized_window_rect(*this));
  739. } else if (type() == WindowType::Desktop) {
  740. set_rect(WindowManager::the().desktop_rect());
  741. } else {
  742. send_event = false;
  743. }
  744. if (send_event) {
  745. Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect));
  746. }
  747. }
  748. void Window::add_child_window(Window& child_window)
  749. {
  750. m_child_windows.append(child_window);
  751. }
  752. void Window::add_accessory_window(Window& accessory_window)
  753. {
  754. m_accessory_windows.append(accessory_window);
  755. }
  756. void Window::set_parent_window(Window& parent_window)
  757. {
  758. VERIFY(!m_parent_window);
  759. m_parent_window = parent_window;
  760. if (m_accessory)
  761. parent_window.add_accessory_window(*this);
  762. else
  763. parent_window.add_child_window(*this);
  764. }
  765. bool Window::is_accessory() const
  766. {
  767. if (!m_accessory)
  768. return false;
  769. if (parent_window() != nullptr)
  770. return true;
  771. // If accessory window was unparented, convert to a regular window
  772. const_cast<Window*>(this)->set_accessory(false);
  773. return false;
  774. }
  775. bool Window::is_accessory_of(Window& window) const
  776. {
  777. if (!is_accessory())
  778. return false;
  779. return parent_window() == &window;
  780. }
  781. void Window::modal_unparented()
  782. {
  783. m_modal = false;
  784. WindowManager::the().notify_modal_unparented(*this);
  785. }
  786. bool Window::is_modal() const
  787. {
  788. if (!m_modal)
  789. return false;
  790. if (!m_parent_window) {
  791. const_cast<Window*>(this)->modal_unparented();
  792. return false;
  793. }
  794. return true;
  795. }
  796. void Window::set_progress(int progress)
  797. {
  798. if (m_progress == progress)
  799. return;
  800. m_progress = progress;
  801. WindowManager::the().notify_progress_changed(*this);
  802. }
  803. bool Window::is_descendant_of(Window& window) const
  804. {
  805. for (auto* parent = parent_window(); parent; parent = parent->parent_window()) {
  806. if (parent == &window)
  807. return true;
  808. for (auto& accessory : parent->accessory_windows()) {
  809. if (accessory == &window)
  810. return true;
  811. }
  812. }
  813. return false;
  814. }
  815. bool Window::hit_test(const Gfx::IntPoint& point, bool include_frame) const
  816. {
  817. if (!frame().rect().contains(point))
  818. return false;
  819. if (!rect().contains(point)) {
  820. if (include_frame)
  821. return frame().hit_test(point);
  822. return false;
  823. }
  824. u8 threshold = alpha_hit_threshold() * 255;
  825. if (threshold == 0 || !m_backing_store || !m_backing_store->has_alpha_channel())
  826. return true;
  827. auto relative_point = point.translated(-rect().location()) * m_backing_store->scale();
  828. auto color = m_backing_store->get_pixel(relative_point);
  829. return color.alpha() >= threshold;
  830. }
  831. void Window::set_menubar(MenuBar* menubar)
  832. {
  833. if (m_menubar == menubar)
  834. return;
  835. m_menubar = menubar;
  836. if (m_menubar) {
  837. auto& wm = WindowManager::the();
  838. Gfx::IntPoint next_menu_location { MenuManager::menubar_menu_margin() / 2, 0 };
  839. auto menubar_rect = Gfx::WindowTheme::current().menu_bar_rect(Gfx::WindowTheme::WindowType::Normal, rect(), wm.palette(), 1);
  840. m_menubar->for_each_menu([&](Menu& menu) {
  841. int text_width = wm.font().width(menu.name());
  842. menu.set_rect_in_window_menubar({ next_menu_location.x() - MenuManager::menubar_menu_margin() / 2, 0, text_width + MenuManager::menubar_menu_margin(), menubar_rect.height() });
  843. Gfx::IntRect text_rect { next_menu_location.translated(0, 1), { text_width, menubar_rect.height() - 3 } };
  844. menu.set_text_rect_in_window_menubar(text_rect);
  845. next_menu_location.move_by(menu.rect_in_window_menubar().width(), 0);
  846. return IterationDecision::Continue;
  847. });
  848. }
  849. Compositor::the().invalidate_occlusions();
  850. frame().invalidate();
  851. }
  852. void Window::invalidate_menubar()
  853. {
  854. if (!menubar())
  855. return;
  856. // FIXME: This invalidates way more than the menubar!
  857. frame().invalidate();
  858. }
  859. }