Serenity Operating System
at master 145 lines 5.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibCore/EventLoop.h> 9#include <LibGUI/Desktop.h> 10#include <LibGUI/Dialog.h> 11#include <LibGUI/Event.h> 12#include <LibGfx/Palette.h> 13 14namespace GUI { 15 16Dialog::Dialog(Window* parent_window, ScreenPosition screen_position) 17 : Window(parent_window) 18 , m_screen_position(screen_position) 19{ 20 set_window_mode(WindowMode::Blocking); 21} 22 23Dialog::ExecResult Dialog::exec() 24{ 25 VERIFY(!m_event_loop); 26 m_event_loop = make<Core::EventLoop>(); 27 28 auto desktop_rect = Desktop::the().rect(); 29 auto window_rect = rect(); 30 31 auto top_align = [](Gfx::Rect<int>& rect) { rect.set_y(32); }; 32 auto bottom_align = [this, desktop_rect](Gfx::Rect<int>& rect) { rect.set_y(desktop_rect.height() - Desktop::the().taskbar_height() - height() - 12); }; 33 34 auto left_align = [](Gfx::Rect<int>& rect) { rect.set_x(12); }; 35 auto right_align = [this, desktop_rect](Gfx::Rect<int>& rect) { rect.set_x(desktop_rect.width() - width() - 12); }; 36 37 switch (m_screen_position) { 38 case ScreenPosition::CenterWithinParent: 39 if (parent() && is<Window>(parent())) { 40 auto& parent_window = *static_cast<Window*>(parent()); 41 if (parent_window.is_visible()) { 42 // Check the dialog's position against the Desktop's rect and reposition it to be entirely visible. 43 // If the dialog is larger than the desktop's rect just center it. 44 window_rect.center_within(parent_window.rect()); 45 if (window_rect.size().width() < desktop_rect.size().width() && window_rect.size().height() < desktop_rect.size().height()) { 46 auto taskbar_top_y = desktop_rect.bottom() - Desktop::the().taskbar_height(); 47 auto palette = GUI::Application::the()->palette(); 48 auto border_thickness = palette.window_border_thickness(); 49 auto top_border_title_thickness = border_thickness + palette.window_title_height(); 50 if (window_rect.top() < top_border_title_thickness) { 51 window_rect.set_y(top_border_title_thickness); 52 } 53 if (window_rect.right() + border_thickness > desktop_rect.right()) { 54 window_rect.translate_by((window_rect.right() + border_thickness - desktop_rect.right()) * -1, 0); 55 } 56 if (window_rect.bottom() + border_thickness > taskbar_top_y) { 57 window_rect.translate_by(0, (window_rect.bottom() + border_thickness - taskbar_top_y) * -1); 58 } 59 if (window_rect.left() - border_thickness < 0) { 60 window_rect.set_x(0 + border_thickness); 61 } 62 } 63 break; 64 } 65 } 66 [[fallthrough]]; // Fall back to `Center` if parent window is invalid or not visible 67 case ScreenPosition::Center: 68 window_rect.center_within(desktop_rect); 69 break; 70 case ScreenPosition::CenterLeft: 71 left_align(window_rect); 72 window_rect.center_vertically_within(desktop_rect); 73 break; 74 case ScreenPosition::CenterRight: 75 right_align(window_rect); 76 window_rect.center_vertically_within(desktop_rect); 77 break; 78 case ScreenPosition::TopLeft: 79 left_align(window_rect); 80 top_align(window_rect); 81 break; 82 case ScreenPosition::TopCenter: 83 window_rect.center_horizontally_within(desktop_rect); 84 top_align(window_rect); 85 break; 86 case ScreenPosition::TopRight: 87 right_align(window_rect); 88 top_align(window_rect); 89 break; 90 case ScreenPosition::BottomLeft: 91 left_align(window_rect); 92 bottom_align(window_rect); 93 break; 94 case ScreenPosition::BottomCenter: 95 window_rect.center_horizontally_within(desktop_rect); 96 bottom_align(window_rect); 97 break; 98 case ScreenPosition::BottomRight: 99 right_align(window_rect); 100 bottom_align(window_rect); 101 break; 102 } 103 104 set_rect(window_rect); 105 show(); 106 auto result = m_event_loop->exec(); 107 m_event_loop = nullptr; 108 dbgln("{}: Event loop returned with result {}", *this, result); 109 remove_from_parent(); 110 return static_cast<ExecResult>(result); 111} 112 113void Dialog::done(ExecResult result) 114{ 115 Window::close(); 116 117 if (!m_event_loop) 118 return; 119 m_result = result; 120 on_done(m_result); 121 122 dbgln("{}: Quit event loop with result {}", *this, to_underlying(result)); 123 m_event_loop->quit(to_underlying(result)); 124} 125 126void Dialog::event(Core::Event& event) 127{ 128 if (event.type() == Event::KeyDown) { 129 auto& key_event = static_cast<KeyEvent&>(event); 130 if (key_event.key() == KeyCode::Key_Escape) { 131 done(ExecResult::Cancel); 132 event.accept(); 133 return; 134 } 135 } 136 137 Window::event(event); 138} 139 140void Dialog::close() 141{ 142 done(ExecResult::Cancel); 143} 144 145}