mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
LibGUI: Implement nested event loops to support dialog boxes.
This patch adds a simple GMessageBox that can run in a nested event loop. Here's how you use it: GMessageBox box("Message text here", "Message window title"); int result = box.exec(); The next step is to make the WindowServer respect the modality flag of these windows and prevent interaction with other windows in the same process until the modal window has been closed.
This commit is contained in:
parent
55aa819077
commit
57ff293a51
Notes:
sideshowbarker
2024-07-19 15:00:43 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/57ff293a51d
29 changed files with 275 additions and 79 deletions
|
@ -14,7 +14,7 @@ int main(int argc, char** argv)
|
|||
Rect window_rect { 0, 0, 240, 120 };
|
||||
window_rect.center_within({ 0, 0, 1024, 768 });
|
||||
window->set_rect(window_rect);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
|
||||
auto* widget = new GWidget;
|
||||
window->set_main_widget(widget);
|
||||
|
|
|
@ -29,7 +29,7 @@ int main(int argc, char** argv)
|
|||
auto* window = new GWindow;
|
||||
window->set_title("FileManager");
|
||||
window->set_rect(20, 200, 640, 480);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
|
||||
auto* widget = new GWidget;
|
||||
widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
|
||||
|
|
|
@ -29,7 +29,7 @@ int main(int argc, char** argv)
|
|||
window->set_rect({ 50, 50, 420, 300 });
|
||||
auto* font_editor = new FontEditorWidget(path, move(edited_font));
|
||||
window->set_main_widget(font_editor);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
window->show();
|
||||
return app.exec();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibGUI/GAction.h>
|
||||
#include <LibGUI/GMenu.h>
|
||||
#include <LibGUI/GMenuBar.h>
|
||||
#include <LibGUI/GMessageBox.h>
|
||||
#include <stdio.h>
|
||||
|
||||
IRCAppWindow::IRCAppWindow()
|
||||
|
@ -40,6 +41,9 @@ void IRCAppWindow::setup_client()
|
|||
};
|
||||
|
||||
m_client.on_connect = [this] {
|
||||
GMessageBox box("We are connected!", "Message");
|
||||
int code = box.exec();
|
||||
dbgprintf("GMessageBox::exec() returned %d\n", code);
|
||||
m_client.join_channel("#test");
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ int main(int argc, char** argv)
|
|||
signal(SIGCHLD, handle_sigchld);
|
||||
|
||||
auto* launcher_window = make_launcher_window();
|
||||
launcher_window->set_should_exit_app_on_close(true);
|
||||
launcher_window->set_should_exit_event_loop_on_close(true);
|
||||
launcher_window->show();
|
||||
|
||||
return app.exec();
|
||||
|
|
|
@ -71,7 +71,7 @@ int main(int argc, char** argv)
|
|||
window->set_title("ProcessManager");
|
||||
window->set_rect(20, 200, 640, 400);
|
||||
window->set_main_widget(widget);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
window->show();
|
||||
|
||||
return app.exec();
|
||||
|
|
|
@ -89,7 +89,7 @@ int main(int argc, char** argv)
|
|||
|
||||
auto* window = new GWindow;
|
||||
window->set_double_buffering_enabled(false);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
|
||||
Terminal terminal(ptm_fd);
|
||||
window->set_has_alpha_channel(true);
|
||||
|
|
|
@ -136,7 +136,7 @@ int main(int argc, char** argv)
|
|||
window->set_title(String::format("TextEditor: %s", path.characters()));
|
||||
window->set_rect(20, 200, 640, 400);
|
||||
window->set_main_widget(widget);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
text_editor->set_focus(true);
|
||||
window->show();
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@ $make_cmd -C ../LibM && \
|
|||
(cd ../LibM && ./install.sh) && \
|
||||
$make_cmd -C ../LibM clean && \
|
||||
$make_cmd -C ../LibM clean && \
|
||||
$make_cmd -C ../WindowServer clean && \
|
||||
$make_cmd -C ../WindowServer && \
|
||||
$make_cmd -C ../LibGUI clean && \
|
||||
$make_cmd -C ../LibGUI && \
|
||||
$make_cmd -C ../Userland clean && \
|
||||
$make_cmd -C ../Userland && \
|
||||
$make_cmd -C ../WindowServer clean && \
|
||||
$make_cmd -C ../WindowServer && \
|
||||
$make_cmd -C ../Applications/Terminal clean && \
|
||||
$make_cmd -C ../Applications/Terminal && \
|
||||
$make_cmd -C ../Applications/FontEditor clean && \
|
||||
|
|
|
@ -37,7 +37,8 @@ void GBoxLayout::run(GWidget& widget)
|
|||
should_log = true;
|
||||
#endif
|
||||
if (should_log)
|
||||
printf("GBoxLayout: running layout on %s{%p}\n", widget.class_name(), &widget);
|
||||
printf("GBoxLayout: running layout on %s{%p}, entry count: %d\n", widget.class_name(), &widget, m_entries.size());
|
||||
|
||||
if (m_entries.is_empty())
|
||||
return;
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ GButton::~GButton()
|
|||
{
|
||||
}
|
||||
|
||||
void GButton::set_caption(String&& caption)
|
||||
void GButton::set_caption(const String& caption)
|
||||
{
|
||||
if (caption == m_caption)
|
||||
return;
|
||||
m_caption = move(caption);
|
||||
m_caption = caption;
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ public:
|
|||
virtual ~GButton() override;
|
||||
|
||||
String caption() const { return m_caption; }
|
||||
void set_caption(String&&);
|
||||
void set_caption(const String&);
|
||||
|
||||
void set_icon(RetainPtr<GraphicsBitmap>&& icon) { m_icon = move(icon); }
|
||||
const GraphicsBitmap* icon() const { return m_icon.ptr(); }
|
||||
|
|
|
@ -19,7 +19,7 @@ String GClipboard::data() const
|
|||
{
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::GetClipboardContents;
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetClipboardContents);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetClipboardContents);
|
||||
if (response.clipboard.shared_buffer_id < 0)
|
||||
return { };
|
||||
auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(response.clipboard.shared_buffer_id);
|
||||
|
@ -38,7 +38,7 @@ void GClipboard::set_data(const String& data)
|
|||
{
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::SetClipboardContents;
|
||||
auto shared_buffer = SharedBuffer::create(GEventLoop::main().server_pid(), data.length() + 1);
|
||||
auto shared_buffer = SharedBuffer::create(GEventLoop::current().server_pid(), data.length() + 1);
|
||||
if (!shared_buffer) {
|
||||
dbgprintf("GClipboard::set_data() failed to create a shared buffer\n");
|
||||
return;
|
||||
|
@ -50,6 +50,6 @@ void GClipboard::set_data(const String& data)
|
|||
shared_buffer->seal();
|
||||
request.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id();
|
||||
request.clipboard.contents_size = data.length();
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents);
|
||||
ASSERT(response.clipboard.shared_buffer_id == shared_buffer->shared_buffer_id());
|
||||
}
|
||||
|
|
26
LibGUI/GDialog.cpp
Normal file
26
LibGUI/GDialog.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <LibGUI/GDialog.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
|
||||
GDialog::GDialog(GObject* parent)
|
||||
: GWindow(parent)
|
||||
{
|
||||
set_modal(true);
|
||||
set_should_exit_event_loop_on_close(true);
|
||||
}
|
||||
|
||||
GDialog::~GDialog()
|
||||
{
|
||||
}
|
||||
|
||||
int GDialog::exec()
|
||||
{
|
||||
GEventLoop loop;
|
||||
show();
|
||||
return loop.exec();
|
||||
}
|
||||
|
||||
void GDialog::done(int result)
|
||||
{
|
||||
m_result = result;
|
||||
GEventLoop::current().quit(result);
|
||||
}
|
19
LibGUI/GDialog.h
Normal file
19
LibGUI/GDialog.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GWindow.h>
|
||||
|
||||
class GDialog : public GWindow {
|
||||
public:
|
||||
virtual ~GDialog() override;
|
||||
|
||||
int exec();
|
||||
|
||||
int result() const { return m_result; }
|
||||
void done(int result);
|
||||
|
||||
protected:
|
||||
explicit GDialog(GObject* parent);
|
||||
|
||||
private:
|
||||
int m_result { 0 };
|
||||
};
|
|
@ -21,11 +21,16 @@
|
|||
|
||||
static HashMap<GShortcut, GAction*>* g_actions;
|
||||
static GEventLoop* s_main_event_loop;
|
||||
static Vector<GEventLoop*>* s_event_loop_stack;
|
||||
int GEventLoop::s_event_fd = -1;
|
||||
pid_t GEventLoop::s_server_pid = -1;
|
||||
HashMap<int, OwnPtr<GEventLoop::EventLoopTimer>>* GEventLoop::s_timers;
|
||||
HashTable<GNotifier*>* GEventLoop::s_notifiers;
|
||||
int GEventLoop::s_next_timer_id = 1;
|
||||
|
||||
void GEventLoop::connect_to_server()
|
||||
{
|
||||
ASSERT(s_event_fd == -1);
|
||||
s_event_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (s_event_fd < 0) {
|
||||
perror("socket");
|
||||
|
@ -51,12 +56,25 @@ void GEventLoop::connect_to_server()
|
|||
if (rc < 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::Greeting;
|
||||
request.greeting.client_pid = getpid();
|
||||
auto response = sync_request(request, WSAPI_ServerMessage::Type::Greeting);
|
||||
s_server_pid = response.greeting.server_pid;
|
||||
}
|
||||
|
||||
GEventLoop::GEventLoop()
|
||||
{
|
||||
if (!s_event_loop_stack) {
|
||||
s_event_loop_stack = new Vector<GEventLoop*>;
|
||||
s_timers = new HashMap<int, OwnPtr<GEventLoop::EventLoopTimer>>;
|
||||
s_notifiers = new HashTable<GNotifier*>;
|
||||
}
|
||||
|
||||
if (!s_main_event_loop) {
|
||||
s_main_event_loop = this;
|
||||
s_event_loop_stack->append(this);
|
||||
connect_to_server();
|
||||
}
|
||||
|
||||
|
@ -78,14 +96,41 @@ GEventLoop& GEventLoop::main()
|
|||
return *s_main_event_loop;
|
||||
}
|
||||
|
||||
GEventLoop& GEventLoop::current()
|
||||
{
|
||||
return *s_event_loop_stack->last();
|
||||
}
|
||||
|
||||
void GEventLoop::quit(int code)
|
||||
{
|
||||
m_exit_requested = true;
|
||||
m_exit_code = code;
|
||||
}
|
||||
|
||||
struct GEventLoopPusher {
|
||||
public:
|
||||
GEventLoopPusher(GEventLoop& event_loop) : m_event_loop(event_loop)
|
||||
{
|
||||
if (&m_event_loop != s_main_event_loop) {
|
||||
m_event_loop.take_pending_events_from(GEventLoop::current());
|
||||
s_event_loop_stack->append(&event_loop);
|
||||
}
|
||||
}
|
||||
~GEventLoopPusher()
|
||||
{
|
||||
if (&m_event_loop != s_main_event_loop) {
|
||||
s_event_loop_stack->take_last();
|
||||
GEventLoop::current().take_pending_events_from(m_event_loop);
|
||||
}
|
||||
}
|
||||
private:
|
||||
GEventLoop& m_event_loop;
|
||||
};
|
||||
|
||||
int GEventLoop::exec()
|
||||
{
|
||||
GEventLoopPusher pusher(*this);
|
||||
|
||||
m_running = true;
|
||||
for (;;) {
|
||||
if (m_exit_requested)
|
||||
|
@ -228,7 +273,7 @@ void GEventLoop::wait_for_event()
|
|||
};
|
||||
|
||||
add_fd_to_set(s_event_fd, rfds);
|
||||
for (auto& notifier : m_notifiers) {
|
||||
for (auto& notifier : *s_notifiers) {
|
||||
if (notifier->event_mask() & GNotifier::Read)
|
||||
add_fd_to_set(notifier->fd(), rfds);
|
||||
if (notifier->event_mask() & GNotifier::Write)
|
||||
|
@ -238,15 +283,15 @@ void GEventLoop::wait_for_event()
|
|||
}
|
||||
|
||||
struct timeval timeout = { 0, 0 };
|
||||
if (!m_timers.is_empty() && m_queued_events.is_empty())
|
||||
if (!s_timers->is_empty() && m_queued_events.is_empty())
|
||||
get_next_timer_expiration(timeout);
|
||||
ASSERT(m_unprocessed_messages.is_empty());
|
||||
int rc = select(max_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && m_timers.is_empty()) ? nullptr : &timeout);
|
||||
int rc = select(max_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && s_timers->is_empty()) ? nullptr : &timeout);
|
||||
if (rc < 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (auto& it : m_timers) {
|
||||
for (auto& it : *s_timers) {
|
||||
auto& timer = *it.value;
|
||||
if (!timer.has_expired())
|
||||
continue;
|
||||
|
@ -262,7 +307,7 @@ void GEventLoop::wait_for_event()
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& notifier : m_notifiers) {
|
||||
for (auto& notifier : *s_notifiers) {
|
||||
if (FD_ISSET(notifier->fd(), &rfds)) {
|
||||
if (notifier->on_ready_to_read)
|
||||
notifier->on_ready_to_read(*notifier);
|
||||
|
@ -378,9 +423,9 @@ void GEventLoop::EventLoopTimer::reload()
|
|||
|
||||
void GEventLoop::get_next_timer_expiration(timeval& soonest)
|
||||
{
|
||||
ASSERT(!m_timers.is_empty());
|
||||
ASSERT(!s_timers->is_empty());
|
||||
bool has_checked_any = false;
|
||||
for (auto& it : m_timers) {
|
||||
for (auto& it : *s_timers) {
|
||||
auto& fire_time = it.value->fire_time;
|
||||
if (!has_checked_any || fire_time.tv_sec < soonest.tv_sec || (fire_time.tv_sec == soonest.tv_sec && fire_time.tv_usec < soonest.tv_usec))
|
||||
soonest = fire_time;
|
||||
|
@ -396,30 +441,30 @@ int GEventLoop::register_timer(GObject& object, int milliseconds, bool should_re
|
|||
timer->interval = milliseconds;
|
||||
timer->reload();
|
||||
timer->should_reload = should_reload;
|
||||
int timer_id = ++m_next_timer_id; // FIXME: This will eventually wrap around.
|
||||
int timer_id = ++s_next_timer_id; // FIXME: This will eventually wrap around.
|
||||
ASSERT(timer_id); // FIXME: Aforementioned wraparound.
|
||||
timer->timer_id = timer_id;
|
||||
m_timers.set(timer->timer_id, move(timer));
|
||||
s_timers->set(timer->timer_id, move(timer));
|
||||
return timer_id;
|
||||
}
|
||||
|
||||
bool GEventLoop::unregister_timer(int timer_id)
|
||||
{
|
||||
auto it = m_timers.find(timer_id);
|
||||
if (it == m_timers.end())
|
||||
auto it = s_timers->find(timer_id);
|
||||
if (it == s_timers->end())
|
||||
return false;
|
||||
m_timers.remove(it);
|
||||
s_timers->remove(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GEventLoop::register_notifier(Badge<GNotifier>, GNotifier& notifier)
|
||||
{
|
||||
m_notifiers.set(¬ifier);
|
||||
s_notifiers->set(¬ifier);
|
||||
}
|
||||
|
||||
void GEventLoop::unregister_notifier(Badge<GNotifier>, GNotifier& notifier)
|
||||
{
|
||||
m_notifiers.remove(¬ifier);
|
||||
s_notifiers->remove(¬ifier);
|
||||
}
|
||||
|
||||
bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message)
|
||||
|
@ -456,7 +501,7 @@ WSAPI_ServerMessage GEventLoop::sync_request(const WSAPI_ClientMessage& request,
|
|||
ASSERT(success);
|
||||
|
||||
WSAPI_ServerMessage response;
|
||||
success = GEventLoop::main().wait_for_specific_event(response_type, response);
|
||||
success = wait_for_specific_event(response_type, response);
|
||||
ASSERT(success);
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -23,14 +23,15 @@ public:
|
|||
void post_event(GObject& receiver, OwnPtr<GEvent>&&);
|
||||
|
||||
static GEventLoop& main();
|
||||
static GEventLoop& current();
|
||||
|
||||
bool running() const { return m_running; }
|
||||
|
||||
int register_timer(GObject&, int milliseconds, bool should_reload);
|
||||
bool unregister_timer(int timer_id);
|
||||
static int register_timer(GObject&, int milliseconds, bool should_reload);
|
||||
static bool unregister_timer(int timer_id);
|
||||
|
||||
void register_notifier(Badge<GNotifier>, GNotifier&);
|
||||
void unregister_notifier(Badge<GNotifier>, GNotifier&);
|
||||
static void register_notifier(Badge<GNotifier>, GNotifier&);
|
||||
static void unregister_notifier(Badge<GNotifier>, GNotifier&);
|
||||
|
||||
void quit(int);
|
||||
|
||||
|
@ -41,6 +42,12 @@ public:
|
|||
|
||||
static pid_t server_pid() { return s_server_pid; }
|
||||
|
||||
void take_pending_events_from(GEventLoop& other)
|
||||
{
|
||||
m_queued_events.append(move(other.m_queued_events));
|
||||
m_unprocessed_messages.append(move(other.m_unprocessed_messages));
|
||||
}
|
||||
|
||||
private:
|
||||
void wait_for_event();
|
||||
bool drain_messages_from_server();
|
||||
|
@ -67,7 +74,6 @@ private:
|
|||
bool m_running { false };
|
||||
bool m_exit_requested { false };
|
||||
int m_exit_code { 0 };
|
||||
int m_next_timer_id { 1 };
|
||||
|
||||
static pid_t s_server_pid;
|
||||
static pid_t s_event_fd;
|
||||
|
@ -83,6 +89,8 @@ private:
|
|||
bool has_expired() const;
|
||||
};
|
||||
|
||||
HashMap<int, OwnPtr<EventLoopTimer>> m_timers;
|
||||
HashTable<GNotifier*> m_notifiers;
|
||||
static HashMap<int, OwnPtr<EventLoopTimer>>* s_timers;
|
||||
static int s_next_timer_id;
|
||||
|
||||
static HashTable<GNotifier*>* s_notifiers;
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@ int GMenu::realize_menu()
|
|||
ASSERT(m_name.length() < (ssize_t)sizeof(request.text));
|
||||
strcpy(request.text, m_name.characters());
|
||||
request.text_length = m_name.length();
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenu);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenu);
|
||||
m_menu_id = response.menu.menu_id;
|
||||
|
||||
ASSERT(m_menu_id > 0);
|
||||
|
@ -56,7 +56,7 @@ int GMenu::realize_menu()
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::AddMenuSeparator;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator);
|
||||
continue;
|
||||
}
|
||||
if (item.type() == GMenuItem::Action) {
|
||||
|
@ -78,7 +78,7 @@ int GMenu::realize_menu()
|
|||
request.menu.shortcut_text_length = 0;
|
||||
}
|
||||
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem);
|
||||
}
|
||||
}
|
||||
all_menus().set(m_menu_id, this);
|
||||
|
@ -93,7 +93,7 @@ void GMenu::unrealize_menu()
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::DestroyMenu;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenu);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenu);
|
||||
m_menu_id = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ int GMenuBar::realize_menubar()
|
|||
{
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::CreateMenubar;
|
||||
WSAPI_ServerMessage response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenubar);
|
||||
WSAPI_ServerMessage response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenubar);
|
||||
return response.menu.menubar_id;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ void GMenuBar::unrealize_menubar()
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::DestroyMenubar;
|
||||
request.menu.menubar_id = m_menubar_id;
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenubar);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenubar);
|
||||
m_menubar_id = 0;
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,12 @@ void GMenuBar::notify_added_to_application(Badge<GApplication>)
|
|||
request.type = WSAPI_ClientMessage::Type::AddMenuToMenubar;
|
||||
request.menu.menubar_id = m_menubar_id;
|
||||
request.menu.menu_id = menu_id;
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuToMenubar);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuToMenubar);
|
||||
}
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::SetApplicationMenubar;
|
||||
request.menu.menubar_id = m_menubar_id;
|
||||
GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidSetApplicationMenubar);
|
||||
GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidSetApplicationMenubar);
|
||||
}
|
||||
|
||||
void GMenuBar::notify_removed_from_application(Badge<GApplication>)
|
||||
|
|
45
LibGUI/GMessageBox.cpp
Normal file
45
LibGUI/GMessageBox.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <LibGUI/GMessageBox.h>
|
||||
#include <LibGUI/GBoxLayout.h>
|
||||
#include <LibGUI/GLabel.h>
|
||||
#include <LibGUI/GButton.h>
|
||||
|
||||
GMessageBox::GMessageBox(const String& text, const String& title, GObject* parent)
|
||||
: GDialog(parent)
|
||||
, m_text(text)
|
||||
{
|
||||
set_title(title);
|
||||
build();
|
||||
}
|
||||
|
||||
GMessageBox::~GMessageBox()
|
||||
{
|
||||
}
|
||||
|
||||
void GMessageBox::build()
|
||||
{
|
||||
auto* widget = new GWidget;
|
||||
set_main_widget(widget);
|
||||
|
||||
int text_width = widget->font().width(m_text);
|
||||
|
||||
set_rect(x(), y(), text_width + 80, 80);
|
||||
|
||||
widget->set_layout(make<GBoxLayout>(Orientation::Vertical));
|
||||
widget->set_fill_with_background_color(true);
|
||||
|
||||
widget->layout()->set_margins({ 0, 15, 0, 15 });
|
||||
widget->layout()->set_spacing(15);
|
||||
|
||||
auto* label = new GLabel(m_text, widget);
|
||||
label->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
|
||||
label->set_preferred_size({ text_width, 16 });
|
||||
|
||||
auto* button = new GButton(widget);
|
||||
button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
|
||||
button->set_preferred_size({ 100, 16 });
|
||||
button->set_caption("OK");
|
||||
button->on_click = [this] (auto&) {
|
||||
dbgprintf("OK button clicked\n");
|
||||
done(0);
|
||||
};
|
||||
}
|
16
LibGUI/GMessageBox.h
Normal file
16
LibGUI/GMessageBox.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GDialog.h>
|
||||
|
||||
class GMessageBox : public GDialog {
|
||||
public:
|
||||
explicit GMessageBox(const String& text, const String& title, GObject* parent = nullptr);
|
||||
virtual ~GMessageBox() override;
|
||||
|
||||
String text() const { return m_text; }
|
||||
|
||||
void build();
|
||||
|
||||
private:
|
||||
String m_text;
|
||||
};
|
|
@ -5,11 +5,11 @@ GNotifier::GNotifier(int fd, unsigned event_mask)
|
|||
: m_fd(fd)
|
||||
, m_event_mask(event_mask)
|
||||
{
|
||||
GEventLoop::main().register_notifier(Badge<GNotifier>(), *this);
|
||||
GEventLoop::register_notifier(Badge<GNotifier>(), *this);
|
||||
}
|
||||
|
||||
GNotifier::~GNotifier()
|
||||
{
|
||||
GEventLoop::main().unregister_notifier(Badge<GNotifier>(), *this);
|
||||
GEventLoop::unregister_notifier(Badge<GNotifier>(), *this);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "GEvent.h"
|
||||
#include "GEventLoop.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GObject::GObject(GObject* parent)
|
||||
: m_parent(parent)
|
||||
|
@ -42,7 +43,7 @@ void GObject::event(GEvent& event)
|
|||
void GObject::add_child(GObject& object)
|
||||
{
|
||||
m_children.append(&object);
|
||||
GEventLoop::main().post_event(*this, make<GChildEvent>(GEvent::ChildAdded, object));
|
||||
GEventLoop::current().post_event(*this, make<GChildEvent>(GEvent::ChildAdded, object));
|
||||
}
|
||||
|
||||
void GObject::remove_child(GObject& object)
|
||||
|
@ -50,7 +51,7 @@ void GObject::remove_child(GObject& object)
|
|||
for (ssize_t i = 0; i < m_children.size(); ++i) {
|
||||
if (m_children[i] == &object) {
|
||||
m_children.remove(i);
|
||||
GEventLoop::main().post_event(*this, make<GChildEvent>(GEvent::ChildRemoved, object));
|
||||
GEventLoop::current().post_event(*this, make<GChildEvent>(GEvent::ChildRemoved, object));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -71,20 +72,32 @@ void GObject::start_timer(int ms)
|
|||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_timer_id = GEventLoop::main().register_timer(*this, ms, true);
|
||||
m_timer_id = GEventLoop::register_timer(*this, ms, true);
|
||||
}
|
||||
|
||||
void GObject::stop_timer()
|
||||
{
|
||||
if (!m_timer_id)
|
||||
return;
|
||||
bool success = GEventLoop::main().unregister_timer(m_timer_id);
|
||||
bool success = GEventLoop::unregister_timer(m_timer_id);
|
||||
ASSERT(success);
|
||||
m_timer_id = 0;
|
||||
}
|
||||
|
||||
void GObject::delete_later()
|
||||
{
|
||||
GEventLoop::main().post_event(*this, make<GEvent>(GEvent::DeferredDestroy));
|
||||
GEventLoop::current().post_event(*this, make<GEvent>(GEvent::DeferredDestroy));
|
||||
}
|
||||
|
||||
void GObject::dump_tree(int indent)
|
||||
{
|
||||
for (int i = 0; i < indent; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%s{%p}\n", class_name(), this);
|
||||
|
||||
for (auto* child : children()) {
|
||||
child->dump_tree(indent + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
|
||||
void delete_later();
|
||||
|
||||
void dump_tree(int indent = 0);
|
||||
|
||||
virtual bool is_widget() const { return false; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -44,10 +44,8 @@ GWindow::~GWindow()
|
|||
|
||||
void GWindow::close()
|
||||
{
|
||||
// FIXME: If we exit the event loop, we're never gonna deal with the delete_later request!
|
||||
// This will become relevant once we support nested event loops.
|
||||
if (should_exit_app_on_close())
|
||||
GEventLoop::main().quit(0);
|
||||
if (should_exit_event_loop_on_close())
|
||||
GEventLoop::current().quit(0);
|
||||
delete_later();
|
||||
}
|
||||
|
||||
|
@ -67,7 +65,7 @@ void GWindow::show()
|
|||
ASSERT(m_title_when_windowless.length() < (ssize_t)sizeof(request.text));
|
||||
strcpy(request.text, m_title_when_windowless.characters());
|
||||
request.text_length = m_title_when_windowless.length();
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow);
|
||||
m_window_id = response.window_id;
|
||||
|
||||
windows().set(m_window_id, this);
|
||||
|
@ -82,12 +80,12 @@ void GWindow::hide()
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::DestroyWindow;
|
||||
request.window_id = m_window_id;
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
void GWindow::set_title(String&& title)
|
||||
void GWindow::set_title(const String& title)
|
||||
{
|
||||
m_title_when_windowless = move(title);
|
||||
m_title_when_windowless = title;
|
||||
if (!m_window_id)
|
||||
return;
|
||||
|
||||
|
@ -97,7 +95,7 @@ void GWindow::set_title(String&& title)
|
|||
ASSERT(m_title_when_windowless.length() < (ssize_t)sizeof(request.text));
|
||||
strcpy(request.text, m_title_when_windowless.characters());
|
||||
request.text_length = m_title_when_windowless.length();
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
String GWindow::title() const
|
||||
|
@ -108,7 +106,7 @@ String GWindow::title() const
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::GetWindowTitle;
|
||||
request.window_id = m_window_id;
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle);
|
||||
return String(response.text, response.text_length);
|
||||
}
|
||||
|
||||
|
@ -120,7 +118,7 @@ Rect GWindow::rect() const
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::GetWindowRect;
|
||||
request.window_id = m_window_id;
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect);
|
||||
auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect);
|
||||
ASSERT(response.window_id == m_window_id);
|
||||
return response.window.rect;
|
||||
}
|
||||
|
@ -128,13 +126,16 @@ Rect GWindow::rect() const
|
|||
void GWindow::set_rect(const Rect& a_rect)
|
||||
{
|
||||
m_rect_when_windowless = a_rect;
|
||||
if (!m_window_id)
|
||||
if (!m_window_id) {
|
||||
if (m_main_widget)
|
||||
m_main_widget->resize(m_rect_when_windowless.size());
|
||||
return;
|
||||
}
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::SetWindowRect;
|
||||
request.window_id = m_window_id;
|
||||
request.window.rect = a_rect;
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
void GWindow::event(GEvent& event)
|
||||
|
@ -185,7 +186,7 @@ void GWindow::event(GEvent& event)
|
|||
message.type = WSAPI_ClientMessage::Type::DidFinishPainting;
|
||||
message.window_id = m_window_id;
|
||||
message.window.rect = rect;
|
||||
GEventLoop::main().post_message_to_server(message);
|
||||
GEventLoop::current().post_message_to_server(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -255,7 +256,7 @@ void GWindow::update(const Rect& a_rect)
|
|||
request.type = WSAPI_ClientMessage::Type::InvalidateRect;
|
||||
request.window_id = m_window_id;
|
||||
request.window.rect = a_rect;
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
void GWindow::set_main_widget(GWidget* widget)
|
||||
|
@ -283,12 +284,12 @@ void GWindow::set_focused_widget(GWidget* widget)
|
|||
if (m_focused_widget == widget)
|
||||
return;
|
||||
if (m_focused_widget) {
|
||||
GEventLoop::main().post_event(*m_focused_widget, make<GEvent>(GEvent::FocusOut));
|
||||
GEventLoop::current().post_event(*m_focused_widget, make<GEvent>(GEvent::FocusOut));
|
||||
m_focused_widget->update();
|
||||
}
|
||||
m_focused_widget = widget;
|
||||
if (m_focused_widget) {
|
||||
GEventLoop::main().post_event(*m_focused_widget, make<GEvent>(GEvent::FocusIn));
|
||||
GEventLoop::current().post_event(*m_focused_widget, make<GEvent>(GEvent::FocusIn));
|
||||
m_focused_widget->update();
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +307,7 @@ void GWindow::set_global_cursor_tracking_widget(GWidget* widget)
|
|||
request.value = widget != nullptr;
|
||||
// FIXME: What if the cursor moves out of our interest range before the server can handle this?
|
||||
// Maybe there could be a response that includes the current cursor location as of enabling.
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
void GWindow::set_has_alpha_channel(bool value)
|
||||
|
@ -331,7 +332,7 @@ void GWindow::set_opacity(float opacity)
|
|||
request.window_id = m_window_id;
|
||||
request.window.opacity = opacity;
|
||||
m_opacity_when_windowless = opacity;
|
||||
GEventLoop::main().post_message_to_server(request);
|
||||
GEventLoop::current().post_message_to_server(request);
|
||||
}
|
||||
|
||||
void GWindow::set_hovered_widget(GWidget* widget)
|
||||
|
@ -340,12 +341,12 @@ void GWindow::set_hovered_widget(GWidget* widget)
|
|||
return;
|
||||
|
||||
if (m_hovered_widget)
|
||||
GEventLoop::main().post_event(*m_hovered_widget, make<GEvent>(GEvent::Leave));
|
||||
GEventLoop::current().post_event(*m_hovered_widget, make<GEvent>(GEvent::Leave));
|
||||
|
||||
m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr;
|
||||
|
||||
if (m_hovered_widget)
|
||||
GEventLoop::main().post_event(*m_hovered_widget, make<GEvent>(GEvent::Enter));
|
||||
GEventLoop::current().post_event(*m_hovered_widget, make<GEvent>(GEvent::Enter));
|
||||
}
|
||||
|
||||
void GWindow::set_current_backing_bitmap(GraphicsBitmap& bitmap, bool flush_immediately)
|
||||
|
@ -359,7 +360,7 @@ void GWindow::set_current_backing_bitmap(GraphicsBitmap& bitmap, bool flush_imme
|
|||
message.backing.has_alpha_channel = bitmap.has_alpha_channel();
|
||||
message.backing.size = bitmap.size();
|
||||
message.backing.flush_immediately = flush_immediately;
|
||||
GEventLoop::main().sync_request(message, WSAPI_ServerMessage::Type::DidSetWindowBackingStore);
|
||||
GEventLoop::current().sync_request(message, WSAPI_ServerMessage::Type::DidSetWindowBackingStore);
|
||||
}
|
||||
|
||||
void GWindow::flip(const Rect& dirty_rect)
|
||||
|
@ -389,3 +390,9 @@ Retained<GraphicsBitmap> GWindow::create_backing_bitmap(const Size& size)
|
|||
auto format = m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32;
|
||||
return GraphicsBitmap::create_with_shared_buffer(format, *shared_buffer, size);
|
||||
}
|
||||
|
||||
void GWindow::set_modal(bool modal)
|
||||
{
|
||||
ASSERT(!m_window_id);
|
||||
m_modal = modal;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ public:
|
|||
|
||||
static GWindow* from_window_id(int);
|
||||
|
||||
bool is_modal() const { return m_modal; }
|
||||
void set_modal(bool);
|
||||
|
||||
void set_double_buffering_enabled(bool);
|
||||
void set_has_alpha_channel(bool);
|
||||
void set_opacity(float);
|
||||
|
@ -22,7 +25,7 @@ public:
|
|||
int window_id() const { return m_window_id; }
|
||||
|
||||
String title() const;
|
||||
void set_title(String&&);
|
||||
void set_title(const String&);
|
||||
|
||||
int x() const { return rect().x(); }
|
||||
int y() const { return rect().y(); }
|
||||
|
@ -62,8 +65,8 @@ public:
|
|||
GWidget* global_cursor_tracking_widget() { return m_global_cursor_tracking_widget.ptr(); }
|
||||
const GWidget* global_cursor_tracking_widget() const { return m_global_cursor_tracking_widget.ptr(); }
|
||||
|
||||
bool should_exit_app_on_close() const { return m_should_exit_app_on_close; }
|
||||
void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; }
|
||||
bool should_exit_event_loop_on_close() const { return m_should_exit_app_on_close; }
|
||||
void set_should_exit_event_loop_on_close(bool b) { m_should_exit_app_on_close = b; }
|
||||
|
||||
GWidget* hovered_widget() { return m_hovered_widget.ptr(); }
|
||||
const GWidget* hovered_widget() const { return m_hovered_widget.ptr(); }
|
||||
|
@ -101,5 +104,6 @@ private:
|
|||
bool m_should_exit_app_on_close { false };
|
||||
bool m_has_alpha_channel { false };
|
||||
bool m_double_buffering_enabled { true };
|
||||
bool m_modal { false };
|
||||
};
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ LIBGUI_OBJS = \
|
|||
GScrollableWidget.o \
|
||||
GSocket.o \
|
||||
GTCPSocket.o \
|
||||
GMessageBox.o \
|
||||
GDialog.o \
|
||||
GWindow.o
|
||||
|
||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
||||
|
|
|
@ -35,7 +35,7 @@ int main(int argc, char** argv)
|
|||
signal(SIGCHLD, handle_sigchld);
|
||||
|
||||
auto* launcher_window = make_launcher_window();
|
||||
launcher_window->set_should_exit_app_on_close(true);
|
||||
launcher_window->set_should_exit_event_loop_on_close(true);
|
||||
launcher_window->show();
|
||||
|
||||
return app.exec();
|
||||
|
|
|
@ -164,6 +164,7 @@ struct WSAPI_ClientMessage {
|
|||
SetWindowBackingStore,
|
||||
GetClipboardContents,
|
||||
SetClipboardContents,
|
||||
Greeting,
|
||||
};
|
||||
Type type { Invalid };
|
||||
int window_id { -1 };
|
||||
|
@ -172,6 +173,9 @@ struct WSAPI_ClientMessage {
|
|||
int value { 0 };
|
||||
|
||||
union {
|
||||
struct {
|
||||
int client_pid;
|
||||
} greeting;
|
||||
struct {
|
||||
int menubar_id;
|
||||
int menu_id;
|
||||
|
|
Loading…
Add table
Reference in a new issue