diff options
Diffstat (limited to 'vcl/unx/gtk3/gtk3gtkinst.cxx')
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 166 |
1 files changed, 147 insertions, 19 deletions
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index 4a0f975b2fd1..04a6f68837ea 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -8899,6 +8899,21 @@ int get_height_rows(int nRowHeight, int nSeparatorHeight, int nRows) return (nRowHeight * nRows) + (nSeparatorHeight * (nRows + 1)); } +tools::Rectangle get_row_area(GtkTreeView* pTreeView, GList* pColumns, GtkTreePath* pPath) +{ + tools::Rectangle aRet; + + GdkRectangle aRect; + for (GList* pEntry = g_list_last(pColumns); pEntry; pEntry = g_list_previous(pEntry)) + { + GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(pEntry->data); + gtk_tree_view_get_cell_area(pTreeView, pPath, pColumn, &aRect); + aRet.Union(tools::Rectangle(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height)); + } + + return aRet; +} + class GtkInstanceTreeView : public GtkInstanceContainer, public virtual weld::TreeView { private: @@ -11042,22 +11057,11 @@ public: virtual tools::Rectangle get_row_area(const weld::TreeIter& rIter) const override { - tools::Rectangle aRet; - const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); GtkTreeModel* pModel = GTK_TREE_MODEL(m_pTreeStore); GtkTreePath* pPath = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter)); - - GdkRectangle aRect; - for (GList* pEntry = g_list_last(m_pColumns); pEntry; pEntry = g_list_previous(pEntry)) - { - GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(pEntry->data); - gtk_tree_view_get_cell_area(m_pTreeView, pPath, pColumn, &aRect); - aRet.Union(tools::Rectangle(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height)); - } - + tools::Rectangle aRet = ::get_row_area(m_pTreeView, m_pColumns, pPath); gtk_tree_path_free(pPath); - return aRet; } @@ -12529,12 +12533,32 @@ struct GtkTreeRowReferenceDeleter } }; +// pop down the toplevel combobox menu when something is activated from a custom +// submenu, i.e. wysiwyg style menu +class CustomRenderMenuButtonHelper : public MenuHelper +{ +private: + GtkToggleButton* m_pComboBox; +public: + CustomRenderMenuButtonHelper(GtkMenu* pMenu, GtkToggleButton* pComboBox) + : MenuHelper(pMenu, false) + , m_pComboBox(pComboBox) + { + } + virtual void signal_activate(GtkMenuItem*) override + { + gtk_toggle_button_set_active(m_pComboBox, false); + } +}; + class GtkInstanceComboBox : public GtkInstanceContainer, public vcl::ISearchableStringList, public virtual weld::ComboBox { private: GtkBuilder* m_pComboBuilder; GtkComboBox* m_pComboBox; + GtkOverlay* m_pOverlay; GtkTreeView* m_pTreeView; + GtkMenuButton* m_pOverlayButton; // button that the StyleDropdown uses on an active row GtkWindow* m_pMenuWindow; GtkTreeModel* m_pTreeModel; GtkCellRenderer* m_pButtonTextRenderer; @@ -12542,11 +12566,14 @@ private: GtkWidget* m_pToggleButton; GtkWidget* m_pEntry; GtkCellView* m_pCellView; + std::unique_ptr<CustomRenderMenuButtonHelper> m_xCustomMenuButtonHelper; std::unique_ptr<vcl::Font> m_xFont; std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter; vcl::QuickSelectionEngine m_aQuickSelectionEngine; std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>> m_aSeparatorRows; + OUString m_sMenuButtonRow; bool m_bHoverSelection; + bool m_bMouseInOverlayButton; bool m_bPopupActive; bool m_bAutoComplete; bool m_bAutoCompleteCaseSensitive; @@ -13296,7 +13323,7 @@ private: void signal_motion() { // if hover-selection was disabled after pressing a key, then turn it back on again - if (!m_bHoverSelection) + if (!m_bHoverSelection && !m_bMouseInOverlayButton) { gtk_tree_view_set_hover_selection(m_pTreeView, true); m_bHoverSelection = true; @@ -13366,8 +13393,6 @@ private: break; } } - -//TODO set_active(0); } while (m_nMRUCount > m_nMaxMRUCount) @@ -13489,12 +13514,70 @@ private: enable_notify_events(); } + static gboolean signalGetChildPosition(GtkOverlay*, GtkWidget*, GdkRectangle* pAllocation, gpointer widget) + { + GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget); + return pThis->signal_get_child_position(pAllocation); + } + + bool signal_get_child_position(GdkRectangle* pAllocation) + { + if (!gtk_widget_get_visible(GTK_WIDGET(m_pOverlayButton))) + return false; + if (!gtk_widget_get_realized(GTK_WIDGET(m_pTreeView))) + return false; + int nRow = find_id_including_mru(m_sMenuButtonRow, true); + if (nRow == -1) + return false; + + gtk_widget_get_preferred_width(GTK_WIDGET(m_pOverlayButton), &pAllocation->width, nullptr); + + GtkTreePath* pPath = gtk_tree_path_new_from_indices(nRow, -1); + GList* pColumns = gtk_tree_view_get_columns(m_pTreeView); + tools::Rectangle aRect = get_row_area(m_pTreeView, pColumns, pPath); + gtk_tree_path_free(pPath); + g_list_free(pColumns); + + pAllocation->x = aRect.Right() - pAllocation->width; + pAllocation->y = aRect.Top(); + pAllocation->height = aRect.GetHeight(); + + return true; + } + + static gboolean signalOverlayButtonCrossing(GtkWidget*, GdkEventCrossing* pEvent, gpointer widget) + { + GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget); + pThis->signal_overlay_button_crossing(pEvent->type == GDK_ENTER_NOTIFY); + return false; + } + + void signal_overlay_button_crossing(bool bEnter) + { + m_bMouseInOverlayButton = bEnter; + if (bEnter) + { + if (m_bHoverSelection) + { + // once toggled button is pressed, turn off hover selection until + // mouse leaves the overlay button + gtk_tree_view_set_hover_selection(m_pTreeView, false); + m_bHoverSelection = false; + } + int nRow = find_id_including_mru(m_sMenuButtonRow, true); + assert(nRow != -1); + tree_view_set_cursor(nRow); // select the buttons row + } + } + public: GtkInstanceComboBox(GtkBuilder* pComboBuilder, GtkComboBox* pComboBox, GtkInstanceBuilder* pBuilder, bool bTakeOwnership) : GtkInstanceContainer(GTK_CONTAINER(gtk_builder_get_object(pComboBuilder, "box")), pBuilder, bTakeOwnership) , m_pComboBuilder(pComboBuilder) , m_pComboBox(pComboBox) + , m_pOverlay(GTK_OVERLAY(gtk_builder_get_object(pComboBuilder, "overlay"))) , m_pTreeView(GTK_TREE_VIEW(gtk_builder_get_object(pComboBuilder, "treeview"))) + , m_pOverlayButton(GTK_MENU_BUTTON(gtk_builder_get_object(pComboBuilder, "overlaybutton"))) , m_pMenuWindow(GTK_WINDOW(gtk_builder_get_object(pComboBuilder, "popup"))) , m_pTreeModel(gtk_combo_box_get_model(pComboBox)) , m_pButtonTextRenderer(nullptr) @@ -13503,6 +13586,7 @@ public: , m_pCellView(nullptr) , m_aQuickSelectionEngine(*this) , m_bHoverSelection(false) + , m_bMouseInOverlayButton(false) , m_bPopupActive(false) , m_bAutoComplete(false) , m_bAutoCompleteCaseSensitive(false) @@ -13615,6 +13699,11 @@ public: // support typeahead for the menu itself, typing into the menu will // select via the vcl selection engine, a matching entry. g_signal_connect(m_pMenuWindow, "key-press-event", G_CALLBACK(signalKeyPress), this); + + g_signal_connect(m_pOverlay, "get-child-position", G_CALLBACK(signalGetChildPosition), this); + gtk_overlay_add_overlay(m_pOverlay, GTK_WIDGET(m_pOverlayButton)); + g_signal_connect(m_pOverlayButton, "leave-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this); + g_signal_connect(m_pOverlayButton, "enter-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this); } virtual int get_active() const override @@ -13986,7 +14075,9 @@ public: virtual bool has_focus() const override { - return gtk_widget_has_focus(m_pToggleButton) || gtk_widget_has_focus(m_pEntry) || GtkInstanceWidget::has_focus(); + return gtk_widget_has_focus(m_pToggleButton) || gtk_widget_has_focus(m_pEntry) || + gtk_widget_has_focus(GTK_WIDGET(m_pOverlayButton)) || + gtk_widget_has_focus(GTK_WIDGET(m_pTreeView)) || GtkInstanceWidget::has_focus(); } virtual bool changed_by_direct_pick() const override @@ -14017,9 +14108,9 @@ public: signal_custom_render(rOutput, rRect, bSelected, rId); } - Size call_signal_custom_get_size(VirtualDevice& rOutput, const OUString& rId) + Size call_signal_custom_get_size(VirtualDevice& rOutput) { - return signal_custom_get_size(rOutput, rId); + return signal_custom_get_size(rOutput); } VclPtr<VirtualDevice> create_render_virtual_device() const override @@ -14027,6 +14118,19 @@ public: return create_virtual_device(); } + virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override + { + m_xCustomMenuButtonHelper.reset(); + GtkInstanceMenu* pPopoverWidget = dynamic_cast<GtkInstanceMenu*>(pMenu); + GtkWidget* pMenuWidget = GTK_WIDGET(pPopoverWidget ? pPopoverWidget->getMenu() : nullptr); + gtk_menu_button_set_popup(m_pOverlayButton, pMenuWidget); + gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), pMenuWidget != nullptr); + gtk_widget_queue_resize_no_redraw(GTK_WIDGET(m_pOverlayButton)); // force location recalc + if (pMenuWidget) + m_xCustomMenuButtonHelper.reset(new CustomRenderMenuButtonHelper(GTK_MENU(pMenuWidget), GTK_TOGGLE_BUTTON(m_pToggleButton))); + m_sMenuButtonRow = OUString::fromUtf8(rIdent); + } + OUString get_mru_entries() const override { const sal_Unicode cSep = ';'; @@ -14073,8 +14177,21 @@ public: m_nMRUCount = nMRUCount; } + int get_menu_button_width() const override + { + bool bVisible = gtk_widget_get_visible(GTK_WIDGET(m_pOverlayButton)); + if (!bVisible) + gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), true); + gint nWidth; + gtk_widget_get_preferred_width(GTK_WIDGET(m_pOverlayButton), &nWidth, nullptr); + if (!bVisible) + gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), false); + return nWidth; + } + virtual ~GtkInstanceComboBox() override { + m_xCustomMenuButtonHelper.reset(); do_clear(); if (m_nAutoCompleteIdleId) g_source_remove(m_nAutoCompleteIdleId); @@ -14129,7 +14246,7 @@ bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell, if (GtkInstanceTreeView* pTreeView = dynamic_cast<GtkInstanceTreeView*>(pWidget)) aSize = pTreeView->call_signal_custom_get_size(*cellsurface->device, sId); else if (GtkInstanceComboBox* pComboBox = dynamic_cast<GtkInstanceComboBox*>(pWidget)) - aSize = pComboBox->call_signal_custom_get_size(*cellsurface->device, sId); + aSize = pComboBox->call_signal_custom_get_size(*cellsurface->device); if (orientation == GTK_ORIENTATION_HORIZONTAL) { @@ -14446,11 +14563,22 @@ public: assert(false && "not implemented"); } + virtual void set_item_menu(const OString&, weld::Menu*) override + { + assert(false && "not implemented"); + } + VclPtr<VirtualDevice> create_render_virtual_device() const override { return create_virtual_device(); } + int get_menu_button_width() const override + { + assert(false && "not implemented"); + return 0; + } + virtual ~GtkInstanceEntryTreeView() override { if (m_nAutoCompleteIdleId) |