Serenity Operating System
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
27#include <LibCore/EventLoop.h>
28#include <LibGUI/Action.h>
29#include <LibGUI/Application.h>
30#include <LibGUI/Desktop.h>
31#include <LibGUI/Label.h>
32#include <LibGUI/MenuBar.h>
33#include <LibGUI/Painter.h>
34#include <LibGUI/Window.h>
35#include <LibGUI/WindowServerConnection.h>
36#include <LibGfx/Font.h>
37#include <LibGfx/Palette.h>
38
39namespace GUI {
40
41static Application* s_the;
42
43Application& Application::the()
44{
45 ASSERT(s_the);
46 return *s_the;
47}
48
49Application::Application(int argc, char** argv)
50{
51 (void)argc;
52 (void)argv;
53 ASSERT(!s_the);
54 s_the = this;
55 m_event_loop = make<Core::EventLoop>();
56 WindowServerConnection::the();
57 if (argc > 0)
58 m_invoked_as = argv[0];
59 for (int i = 1; i < argc; i++)
60 m_args.append(argv[i]);
61}
62
63Application::~Application()
64{
65 s_the = nullptr;
66}
67
68int Application::exec()
69{
70 int exit_code = m_event_loop->exec();
71 // NOTE: Maybe it would be cool to return instead of exit()?
72 // This would require cleaning up all the CObjects on the heap.
73 exit(exit_code);
74 return exit_code;
75}
76
77void Application::quit(int exit_code)
78{
79 m_event_loop->quit(exit_code);
80}
81
82void Application::set_menubar(OwnPtr<MenuBar>&& menubar)
83{
84 if (m_menubar)
85 m_menubar->notify_removed_from_application({});
86 m_menubar = move(menubar);
87 if (m_menubar)
88 m_menubar->notify_added_to_application({});
89}
90
91void Application::register_global_shortcut_action(Badge<Action>, Action& action)
92{
93 m_global_shortcut_actions.set(action.shortcut(), &action);
94}
95
96void Application::unregister_global_shortcut_action(Badge<Action>, Action& action)
97{
98 m_global_shortcut_actions.remove(action.shortcut());
99}
100
101Action* Application::action_for_key_event(const KeyEvent& event)
102{
103 auto it = m_global_shortcut_actions.find(Shortcut(event.modifiers(), (KeyCode)event.key()));
104 if (it == m_global_shortcut_actions.end())
105 return nullptr;
106 return (*it).value;
107}
108
109class Application::TooltipWindow final : public Window {
110 C_OBJECT(TooltipWindow);
111
112public:
113 void set_tooltip(const StringView& tooltip)
114 {
115 // FIXME: Add some kind of GLabel auto-sizing feature.
116 int text_width = m_label->font().width(tooltip);
117 set_rect(100, 100, text_width + 10, m_label->font().glyph_height() + 8);
118 m_label->set_text(tooltip);
119 }
120
121private:
122 TooltipWindow()
123 {
124 set_window_type(WindowType::Tooltip);
125 m_label = Label::construct();
126 m_label->set_background_color(Color::from_rgb(0xdac7b5));
127 m_label->set_fill_with_background_color(true);
128 m_label->set_frame_thickness(1);
129 m_label->set_frame_shape(Gfx::FrameShape::Container);
130 m_label->set_frame_shadow(Gfx::FrameShadow::Plain);
131 set_main_widget(m_label);
132 }
133
134 RefPtr<Label> m_label;
135};
136
137void Application::show_tooltip(const StringView& tooltip, const Gfx::Point& screen_location)
138{
139 if (!m_tooltip_window) {
140 m_tooltip_window = TooltipWindow::construct();
141 m_tooltip_window->set_double_buffering_enabled(false);
142 }
143 m_tooltip_window->set_tooltip(tooltip);
144
145 Gfx::Rect desktop_rect = Desktop::the().rect();
146
147 const int margin = 30;
148 Gfx::Point adjusted_pos = screen_location;
149 if (adjusted_pos.x() + m_tooltip_window->width() >= desktop_rect.width() - margin) {
150 adjusted_pos = adjusted_pos.translated(-m_tooltip_window->width(), 0);
151 }
152 if (adjusted_pos.y() + m_tooltip_window->height() >= desktop_rect.height() - margin) {
153 adjusted_pos = adjusted_pos.translated(0, -(m_tooltip_window->height() * 2));
154 }
155
156 m_tooltip_window->move_to(adjusted_pos);
157 m_tooltip_window->show();
158}
159
160void Application::hide_tooltip()
161{
162 if (m_tooltip_window) {
163 m_tooltip_window->hide();
164 m_tooltip_window = nullptr;
165 }
166}
167
168void Application::did_create_window(Badge<Window>)
169{
170 if (m_event_loop->was_exit_requested())
171 m_event_loop->unquit();
172}
173
174void Application::did_delete_last_window(Badge<Window>)
175{
176 if (m_quit_when_last_window_deleted)
177 m_event_loop->quit(0);
178}
179
180void Application::set_system_palette(SharedBuffer& buffer)
181{
182 if (!m_system_palette)
183 m_system_palette = Gfx::PaletteImpl::create_with_shared_buffer(buffer);
184 else
185 m_system_palette->replace_internal_buffer({}, buffer);
186
187 if (!m_palette)
188 m_palette = m_system_palette;
189}
190
191void Application::set_palette(const Palette& palette)
192{
193 m_palette = palette.impl();
194}
195
196Gfx::Palette Application::palette() const
197{
198 return Palette(*m_palette);
199}
200
201}