Skip to content

Commit cb792bb

Browse files
committed
Implement GTK message dialog for Linux
Adds a full implementation of MessageDialog::Impl using GTK on Linux. The dialog now supports setting title and message, opening with different modalities, and proper cleanup. Modal and non-modal behaviors are handled, and signal connections ensure dialog state is tracked and resources are released.
1 parent 16c31a8 commit cb792bb

File tree

1 file changed

+118
-9
lines changed

1 file changed

+118
-9
lines changed

src/platform/linux/message_dialog_linux.cpp

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
#include "../../message_dialog.h"
22
#include "../../dialog.h"
33

4+
#include <gtk/gtk.h>
5+
46
namespace nativeapi {
57

6-
// Private implementation class for MessageDialog (Linux stub)
8+
// Private implementation class for MessageDialog
79
class MessageDialog::Impl {
810
public:
911
Impl(const std::string& title, const std::string& message)
10-
: title_(title), message_(message) {
11-
// TODO: Implement GTK message dialog (GtkMessageDialog or GtkDialog)
12+
: title_(title), message_(message), dialog_(nullptr), is_open_(false) {
13+
// Ensure GTK is initialized
14+
if (!gdk_display_get_default()) {
15+
gtk_init_check(nullptr, nullptr);
16+
}
1217
}
1318

1419
~Impl() {
15-
// TODO: Cleanup if needed
20+
if (dialog_) {
21+
gtk_widget_destroy(dialog_);
22+
dialog_ = nullptr;
23+
}
1624
}
1725

1826
void SetTitle(const std::string& title) {
1927
title_ = title;
28+
// Title will be applied when dialog is opened
2029
}
2130

2231
std::string GetTitle() const {
@@ -25,26 +34,126 @@ class MessageDialog::Impl {
2534

2635
void SetMessage(const std::string& message) {
2736
message_ = message;
37+
// Message will be applied when dialog is opened
2838
}
2939

3040
std::string GetMessage() const {
3141
return message_;
3242
}
3343

3444
bool Open(DialogModality modality) {
35-
// TODO: Implement using gtk_message_dialog_new or gtk_dialog_new
36-
// For now, return false (not implemented)
37-
return false;
45+
// Ensure GTK is initialized
46+
if (!gdk_display_get_default()) {
47+
if (!gtk_init_check(nullptr, nullptr)) {
48+
return false;
49+
}
50+
}
51+
52+
// For modal dialogs, always create a new dialog since gtk_dialog_run destroys it
53+
// For non-modal dialogs, close existing dialog if open before creating new one
54+
// This ensures title and message are always up to date
55+
if (dialog_ && GTK_IS_WIDGET(dialog_)) {
56+
// Destroy old dialog if exists
57+
gtk_widget_destroy(dialog_);
58+
dialog_ = nullptr;
59+
is_open_ = false;
60+
}
61+
62+
// Create a new message dialog
63+
dialog_ = gtk_message_dialog_new(
64+
nullptr, // No parent window
65+
GTK_DIALOG_MODAL, // Default to modal (will be overridden for non-modal)
66+
GTK_MESSAGE_INFO, // Message type
67+
GTK_BUTTONS_OK, // Buttons
68+
"%s", // Format string
69+
message_.c_str());
70+
71+
if (!dialog_) {
72+
return false;
73+
}
74+
75+
// Set title
76+
gtk_window_set_title(GTK_WINDOW(dialog_), title_.c_str());
77+
78+
// Connect destroy signal to track when dialog is closed by user
79+
g_signal_connect(dialog_, "response", G_CALLBACK(OnResponse), this);
80+
g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this);
81+
82+
// Handle modality
83+
switch (modality) {
84+
case DialogModality::None:
85+
// Non-modal: show the dialog without blocking
86+
gtk_window_set_modal(GTK_WINDOW(dialog_), FALSE);
87+
gtk_widget_show(dialog_);
88+
is_open_ = true;
89+
break;
90+
91+
case DialogModality::Application:
92+
case DialogModality::Window:
93+
// Modal: block until user responds
94+
gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
95+
is_open_ = true;
96+
97+
// Run the dialog modally - this blocks until user responds
98+
// Note: gtk_dialog_run automatically destroys the dialog when done
99+
gtk_dialog_run(GTK_DIALOG(dialog_));
100+
101+
// After gtk_dialog_run returns, the dialog has been dismissed and destroyed
102+
// The OnDestroy callback will set dialog_ to nullptr
103+
is_open_ = false;
104+
break;
105+
}
106+
107+
return true;
38108
}
39109

40110
bool Close() {
41-
// TODO: Implement closing logic using gtk_widget_destroy
42-
return false;
111+
if (!dialog_ || !is_open_) {
112+
return false;
113+
}
114+
115+
// Close the dialog programmatically
116+
gtk_dialog_response(GTK_DIALOG(dialog_), GTK_RESPONSE_CLOSE);
117+
118+
// If it's a modal dialog, we need to destroy it manually
119+
// (gtk_dialog_run already destroyed it)
120+
if (dialog_) {
121+
gtk_widget_destroy(dialog_);
122+
dialog_ = nullptr;
123+
}
124+
125+
is_open_ = false;
126+
return true;
43127
}
44128

45129
private:
46130
std::string title_;
47131
std::string message_;
132+
GtkWidget* dialog_;
133+
bool is_open_;
134+
135+
static void OnResponse(GtkDialog* dialog, gint response_id, gpointer user_data) {
136+
Impl* impl = static_cast<Impl*>(user_data);
137+
138+
// Mark as closed
139+
impl->is_open_ = false;
140+
141+
// For non-modal dialogs, destroy the dialog when user responds
142+
if (!gtk_window_get_modal(GTK_WINDOW(dialog))) {
143+
gtk_widget_destroy(GTK_WIDGET(dialog));
144+
impl->dialog_ = nullptr;
145+
}
146+
}
147+
148+
static void OnDestroy(GtkWidget* widget, gpointer user_data) {
149+
Impl* impl = static_cast<Impl*>(user_data);
150+
151+
// Clear the dialog pointer when it's destroyed
152+
if (impl->dialog_ == widget) {
153+
impl->dialog_ = nullptr;
154+
impl->is_open_ = false;
155+
}
156+
}
48157
};
49158

50159
// MessageDialog implementation

0 commit comments

Comments
 (0)