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 Core::Objects 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 GUI::Label 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 = set_main_widget<Label>();
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 }
132
133 RefPtr<Label> m_label;
134};
135
136void Application::show_tooltip(const StringView& tooltip, const Gfx::Point& screen_location)
137{
138 if (!m_tooltip_window) {
139 m_tooltip_window = TooltipWindow::construct();
140 m_tooltip_window->set_double_buffering_enabled(false);
141 }
142 m_tooltip_window->set_tooltip(tooltip);
143
144 Gfx::Rect desktop_rect = Desktop::the().rect();
145
146 const int margin = 30;
147 Gfx::Point adjusted_pos = screen_location;
148 if (adjusted_pos.x() + m_tooltip_window->width() >= desktop_rect.width() - margin) {
149 adjusted_pos = adjusted_pos.translated(-m_tooltip_window->width(), 0);
150 }
151 if (adjusted_pos.y() + m_tooltip_window->height() >= desktop_rect.height() - margin) {
152 adjusted_pos = adjusted_pos.translated(0, -(m_tooltip_window->height() * 2));
153 }
154
155 m_tooltip_window->move_to(adjusted_pos);
156 m_tooltip_window->show();
157}
158
159void Application::hide_tooltip()
160{
161 if (m_tooltip_window) {
162 m_tooltip_window->hide();
163 m_tooltip_window = nullptr;
164 }
165}
166
167void Application::did_create_window(Badge<Window>)
168{
169 if (m_event_loop->was_exit_requested())
170 m_event_loop->unquit();
171}
172
173void Application::did_delete_last_window(Badge<Window>)
174{
175 if (m_quit_when_last_window_deleted)
176 m_event_loop->quit(0);
177}
178
179void Application::set_system_palette(SharedBuffer& buffer)
180{
181 if (!m_system_palette)
182 m_system_palette = Gfx::PaletteImpl::create_with_shared_buffer(buffer);
183 else
184 m_system_palette->replace_internal_buffer({}, buffer);
185
186 if (!m_palette)
187 m_palette = m_system_palette;
188}
189
190void Application::set_palette(const Palette& palette)
191{
192 m_palette = palette.impl();
193}
194
195Gfx::Palette Application::palette() const
196{
197 return Palette(*m_palette);
198}
199
200}