diff options
author | Caolán McNamara <caolanm@redhat.com> | 2019-05-14 21:50:36 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2019-05-17 10:59:44 +0200 |
commit | 644ca26af744aec1e66c8dd4199d1228e0f780be (patch) | |
tree | 2a364e77b595a65b9f29ce076b5b9352c9ccdbea /vcl | |
parent | dc17f247daeb281dda531ff335873d563db5b653 (diff) |
allow closing welded dialogs to be cancelled
from both directions of window manager close button and esc
Change-Id: I3c7bd6f67e3100f5dd3ab04456ad9aa5100c472c
Reviewed-on: https://gerrit.libreoffice.org/72319
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/source/window/dialog.cxx | 18 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 141 |
2 files changed, 114 insertions, 45 deletions
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx index 21957995644e..5348e206f2c0 100644 --- a/vcl/source/window/dialog.cxx +++ b/vcl/source/window/dialog.cxx @@ -835,8 +835,26 @@ bool Dialog::Close() if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() && !IsInExecute() ) return false; + // If there's a cancel button with a custom handler, then always give it a chance to + // handle Dialog::Close + PushButton* pCustomCancelButton; + PushButton* pCancelButton = dynamic_cast<PushButton*>(get_widget_for_response(RET_CANCEL)); + if (!mbInClose && pCancelButton && pCancelButton->GetClickHdl().IsSet()) + pCustomCancelButton = pCancelButton; + else + pCustomCancelButton = nullptr; + mbInClose = true; + if (pCustomCancelButton) + { + pCustomCancelButton->Click(); + if (xWindow->IsDisposed()) + return true; + mbInClose = false; + return false; + } + if ( !(GetStyle() & WB_CLOSEABLE) ) { bool bRet = true; diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index dac1bccae151..0d60969bff8d 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -2995,6 +2995,8 @@ namespace } } +class GtkInstanceButton; + class GtkInstanceDialog : public GtkInstanceWindow, public virtual weld::Dialog { private: @@ -3006,6 +3008,7 @@ private: std::function<void(sal_Int32)> m_aFunc; gulong m_nCloseSignalId; gulong m_nResponseSignalId; + gulong m_nSignalDeleteId; // for calc ref dialog that shrink to range selection widgets and resize back GtkWidget* m_pRefEdit; @@ -3014,10 +3017,12 @@ private: int m_nOldEditWidthReq; // Original width request of the input field int m_nOldBorderWidth; // border width for expanded dialog + void signal_close(); + static void signalClose(GtkWidget*, gpointer widget) { GtkInstanceDialog* pThis = static_cast<GtkInstanceDialog*>(widget); - pThis->response(RET_CANCEL); + pThis->signal_close(); } static void signalAsyncResponse(GtkWidget*, gint ret, gpointer widget) @@ -3026,6 +3031,11 @@ private: pThis->asyncresponse(ret); } + static gboolean signalAsyncDelete(GtkDialog*, GdkEventAny*, gpointer) + { + return true; /* Do not destroy */ + } + static int GtkToVcl(int ret) { if (ret == GTK_RESPONSE_OK) @@ -3043,24 +3053,7 @@ private: return ret; } - void asyncresponse(gint ret) - { - if (ret == GTK_RESPONSE_HELP) - { - help(); - return; - } - else if (has_click_handler(ret)) - return; - - hide(); - m_aFunc(GtkToVcl(ret)); - m_aFunc = nullptr; - // move the self pointer, otherwise it might be de-allocated by time we try to reset it - std::shared_ptr<GtkInstanceDialog> me = std::move(m_xRunAsyncSelf); - m_xDialogController.reset(); - me.reset(); - } + void asyncresponse(gint ret); public: GtkInstanceDialog(GtkDialog* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership) @@ -3069,6 +3062,7 @@ public: , m_aDialogRun(pDialog) , m_nCloseSignalId(g_signal_connect(m_pDialog, "close", G_CALLBACK(signalClose), this)) , m_nResponseSignalId(0) + , m_nSignalDeleteId(0) , m_pRefEdit(nullptr) , m_nOldEditWidth(0) , m_nOldEditWidthReq(0) @@ -3086,6 +3080,7 @@ public: show(); m_nResponseSignalId = g_signal_connect(m_pDialog, "response", G_CALLBACK(signalAsyncResponse), this); + m_nSignalDeleteId = g_signal_connect(m_pDialog, "delete-event", G_CALLBACK(signalAsyncDelete), this); return true; } @@ -3100,32 +3095,14 @@ public: show(); m_nResponseSignalId = g_signal_connect(m_pDialog, "response", G_CALLBACK(signalAsyncResponse), this); + m_nSignalDeleteId = g_signal_connect(m_pDialog, "delete-event", G_CALLBACK(signalAsyncDelete), this); return true; } - bool has_click_handler(int nResponse); + GtkInstanceButton* has_click_handler(int nResponse); - virtual int run() override - { - sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog))); - int ret; - while (true) - { - ret = m_aDialogRun.run(); - if (ret == GTK_RESPONSE_HELP) - { - help(); - continue; - } - else if (has_click_handler(ret)) - continue; - - break; - } - hide(); - return GtkToVcl(ret); - } + virtual int run() override; virtual void show() override { @@ -3295,6 +3272,8 @@ public: g_signal_handler_disconnect(m_pDialog, m_nCloseSignalId); if (m_nResponseSignalId) g_signal_handler_disconnect(m_pDialog, m_nResponseSignalId); + if (m_nSignalDeleteId) + g_signal_handler_disconnect(m_pDialog, m_nSignalDeleteId); } }; @@ -4610,6 +4589,60 @@ public: } }; +void GtkInstanceDialog::asyncresponse(gint ret) +{ + if (ret == GTK_RESPONSE_HELP) + { + help(); + return; + } + + GtkInstanceButton* pClickHandler = has_click_handler(ret); + if (pClickHandler) + { + // make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed + if (ret == GTK_RESPONSE_DELETE_EVENT) + pClickHandler->clicked(); + return; + } + + hide(); + m_aFunc(GtkToVcl(ret)); + m_aFunc = nullptr; + // move the self pointer, otherwise it might be de-allocated by time we try to reset it + std::shared_ptr<GtkInstanceDialog> me = std::move(m_xRunAsyncSelf); + m_xDialogController.reset(); + me.reset(); +} + +int GtkInstanceDialog::run() +{ + sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog))); + int ret; + while (true) + { + ret = m_aDialogRun.run(); + if (ret == GTK_RESPONSE_HELP) + { + help(); + continue; + } + + GtkInstanceButton* pClickHandler = has_click_handler(ret); + if (pClickHandler) + { + // make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed + if (ret == GTK_RESPONSE_DELETE_EVENT) + pClickHandler->clicked(); + continue; + } + + break; + } + hide(); + return GtkToVcl(ret); +} + weld::Button* GtkInstanceDialog::get_widget_for_response(int nResponse) { GtkButton* pButton = GTK_BUTTON(gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse))); @@ -4631,15 +4664,33 @@ void GtkInstanceDialog::response(int nResponse) gtk_dialog_response(m_pDialog, VclToGtk(nResponse)); } -bool GtkInstanceDialog::has_click_handler(int nResponse) + +void GtkInstanceDialog::signal_close() { - if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse))) + GtkInstanceButton* pClickHandler = has_click_handler(GTK_RESPONSE_CANCEL); + if (pClickHandler) + { + g_signal_stop_emission_by_name(m_pDialog, "close"); + // make esc act as if cancel button was pressed + pClickHandler->clicked(); + return; + } + response(RET_CANCEL); +} + +GtkInstanceButton* GtkInstanceDialog::has_click_handler(int nResponse) +{ + GtkInstanceButton* pButton = nullptr; + // e.g. map GTK_RESPONSE_DELETE_EVENT to GTK_RESPONSE_CANCEL + nResponse = VclToGtk(GtkToVcl(nResponse)); + if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, nResponse)) { void* pData = g_object_get_data(G_OBJECT(pWidget), "g-lo-GtkInstanceButton"); - GtkInstanceButton* pButton = static_cast<GtkInstanceButton*>(pData); - return pButton && pButton->has_click_handler(); + pButton = static_cast<GtkInstanceButton*>(pData); + if (pButton && !pButton->has_click_handler()) + pButton = nullptr; } - return false; + return pButton; } class GtkInstanceToggleButton : public GtkInstanceButton, public virtual weld::ToggleButton |