for DisallowCycleFocusOut forward unused keyevents to InterimItemWindow
for ctrl+q etc
Change-Id: Ie0ad94cf0e85693960428ffee5ae4a0ecffb7c6b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/104635
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 27865f2..820d796 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -530,6 +530,8 @@ public:
static OUString GetPreeditDetails(GtkIMContext* pIMContext, std::vector<ExtTextInputAttr>& rInputFlags, sal_Int32& rCursorPos, sal_uInt8& rCursorFlags);
void DisallowCycleFocusOut();
bool IsCycleFocusOutDisallowed() const;
void AllowCycleFocusOut();
};
#define OOO_TYPE_FIXED ooo_fixed_get_type()
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
index 26c34a0..eb964a7 100644
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
@@ -1026,6 +1026,26 @@ void GtkSalFrame::DisallowCycleFocusOut()
gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), false);
}
bool GtkSalFrame::IsCycleFocusOutDisallowed() const
{
return m_nSetFocusSignalId == 0;
}
void GtkSalFrame::AllowCycleFocusOut()
{
if (m_nSetFocusSignalId)
return;
// enable/disable can-focus as control enters and leaves
// embedded native gtk widgets
m_nSetFocusSignalId = g_signal_connect(G_OBJECT(m_pWindow), "set-focus", G_CALLBACK(signalSetFocus), this);
// set container without can-focus and focus will tab between
// the native embedded widgets using the default gtk handling for
// that
gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true);
}
void GtkSalFrame::Init( SalFrame* pParent, SalFrameStyleFlags nStyle )
{
if( nStyle & SalFrameStyleFlags::DEFAULT ) // ensure default style
@@ -3198,6 +3218,8 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
bool bFocusInAnotherGtkWidget = false;
VclPtr<vcl::Window> xTopLevelInterimWindow;
if (GTK_IS_WINDOW(pThis->m_pWindow))
{
GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(pThis->m_pWindow));
@@ -3216,6 +3238,24 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
g_type_class_unref(pClass);
if (bHandled)
return true;
// Is focus inside a full-app InterimItemWindow? In which case find
// that InterimItemWindow and send unconsumed keystrokes to it to
// support ctrl-q etc shortcuts
if (pThis->IsCycleFocusOutDisallowed())
{
GtkWidget* pSearch = pFocusWindow;
while (pSearch)
{
void* pData = g_object_get_data(G_OBJECT(pSearch), "InterimWindowGlue");
if (pData)
{
xTopLevelInterimWindow = static_cast<vcl::Window*>(pData);
break;
}
pSearch = gtk_widget_get_parent(pSearch);
}
}
}
}
@@ -3303,6 +3343,22 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
}
else
{
VclPtr<vcl::Window> xOrigFocusWin;
if (xTopLevelInterimWindow)
{
// Focus is inside a full-app InterimItemWindow send unconsumed
// keystrokes to by setting it as the mpFocusWin
VclPtr<vcl::Window> xVclWindow = pThis->GetWindow();
ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData;
xOrigFocusWin = pFrameData->mpFocusWin;
pFrameData->mpFocusWin = xTopLevelInterimWindow;
if (pEvent->keyval == GDK_KEY_F6)
{
// For F6, allow the focus to leave the InterimItemWindow
pThis->AllowCycleFocusOut();
}
}
bStopProcessingKey = pThis->doKeyCallback(pEvent->state,
pEvent->keyval,
pEvent->hardware_keycode,
@@ -3310,8 +3366,27 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )),
(pEvent->type == GDK_KEY_PRESS),
false);
if( ! aDel.isDeleted() )
if (!aDel.isDeleted())
{
pThis->m_nKeyModifiers = ModKeyFlags::NONE;
if (xTopLevelInterimWindow)
{
// Focus was inside a full-app InterimItemWindow, restore the original
// focus win, unless the focus was changed away from the InterimItemWindow
// which should only be possible with F6
VclPtr<vcl::Window> xVclWindow = pThis->GetWindow();
ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData;
if (pFrameData->mpFocusWin == xTopLevelInterimWindow)
pFrameData->mpFocusWin = xOrigFocusWin;
if (pEvent->keyval == GDK_KEY_F6)
{
// undo the above AllowCycleFocusOut for F6
pThis->DisallowCycleFocusOut();
}
}
}
}
if (!bFocusInAnotherGtkWidget && !aDel.isDeleted() && pThis->m_pIMHandler)
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 6448e37..13f97ce 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -16291,6 +16291,7 @@ private:
std::vector<GtkLabel*> m_aMnemonicLabels;
VclPtr<SystemChildWindow> m_xInterimGlue;
bool m_bAllowCycleFocusOut;
void postprocess_widget(GtkWidget* pWidget)
{
@@ -16458,12 +16459,14 @@ private:
pThis->postprocess_widget(GTK_WIDGET(pObject));
}
public:
GtkInstanceBuilder(GtkWidget* pParent, const OUString& rUIRoot, const OUString& rUIFile, SystemChildWindow* pInterimGlue)
GtkInstanceBuilder(GtkWidget* pParent, const OUString& rUIRoot, const OUString& rUIFile,
SystemChildWindow* pInterimGlue, bool bAllowCycleFocusOut)
: weld::Builder()
, m_pStringReplace(Translate::GetReadStringHook())
, m_pParentWidget(pParent)
, m_nNotifySignalId(0)
, m_xInterimGlue(pInterimGlue)
, m_bAllowCycleFocusOut(bAllowCycleFocusOut)
{
OUString sHelpRoot(rUIFile);
ensure_intercept_drawing_area_accessibility();
@@ -16495,6 +16498,17 @@ public:
{
assert(m_pParentWidget);
g_object_set_data(G_OBJECT(m_pParentWidget), "InterimWindowGlue", m_xInterimGlue.get());
if (!m_bAllowCycleFocusOut)
{
GtkWidget* pTopLevel = gtk_widget_get_toplevel(m_pParentWidget);
assert(pTopLevel);
GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
assert(pFrame);
// unhook handler and let gtk cycle its own way through this widget's
// children because it has no non-gtk siblings
pFrame->DisallowCycleFocusOut();
}
}
}
@@ -16555,6 +16569,18 @@ public:
{
g_slist_free(m_pObjectList);
g_object_unref(m_pBuilder);
if (m_xInterimGlue && !m_bAllowCycleFocusOut)
{
GtkWidget* pTopLevel = gtk_widget_get_toplevel(m_pParentWidget);
assert(pTopLevel);
GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
assert(pFrame);
// rehook handler and let vcl cycle its own way through this widget's
// children
pFrame->AllowCycleFocusOut();
}
m_xInterimGlue.disposeAndClear();
}
@@ -17048,7 +17074,7 @@ weld::Builder* GtkInstance::CreateBuilder(weld::Widget* pParent, const OUString&
if (pParent && !pParentWidget) //remove when complete
return SalInstance::CreateBuilder(pParent, rUIRoot, rUIFile);
GtkWidget* pBuilderParent = pParentWidget ? pParentWidget->getWidget() : nullptr;
return new GtkInstanceBuilder(pBuilderParent, rUIRoot, rUIFile, nullptr);
return new GtkInstanceBuilder(pBuilderParent, rUIRoot, rUIFile, nullptr, true);
}
// tdf#135965 for the case of native widgets inside a GtkSalFrame and F1 pressed, run help
@@ -17122,19 +17148,8 @@ weld::Builder* GtkInstance::CreateInterimBuilder(vcl::Window* pParent, const OUS
GtkWidget *pWindow = static_cast<GtkWidget*>(pEnvData->pWidget);
gtk_widget_show_all(pWindow);
if (!bAllowCycleFocusOut)
{
GtkWidget* pTopLevel = gtk_widget_get_toplevel(pWindow);
assert(pTopLevel);
GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
assert(pFrame);
// unhook handler and let gtk cycle its own way through this widget's
// children because it has no non-gtk siblings
pFrame->DisallowCycleFocusOut();
}
// build the widget tree as a child of the GtkEventBox GtkGrid parent
return new GtkInstanceBuilder(pWindow, rUIRoot, rUIFile, xEmbedWindow.get());
return new GtkInstanceBuilder(pWindow, rUIRoot, rUIFile, xEmbedWindow.get(), bAllowCycleFocusOut);
}
weld::MessageDialog* GtkInstance::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType, VclButtonsType eButtonsType, const OUString &rPrimaryMessage)