From f30f094a6c014976e6930ee6f4e8aa6d007e9c06 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sat, 10 Apr 2021 17:41:33 -0700 Subject: [PATCH] Improve pop-up common with gicons and item ops Add ability to use gicon images, insert pop-up entries in any location, and remove entries. --- rtgui/popupcommon.cc | 157 +++++++++++++++++++++++++++++++++---------- rtgui/popupcommon.h | 19 +++++- 2 files changed, 141 insertions(+), 35 deletions(-) diff --git a/rtgui/popupcommon.cc b/rtgui/popupcommon.cc index 8c4c9dda1..fabc4d572 100644 --- a/rtgui/popupcommon.cc +++ b/rtgui/popupcommon.cc @@ -27,7 +27,7 @@ PopUpCommon::PopUpCommon (Gtk::Button* thisButton, const Glib::ustring& label) : buttonImage (nullptr) - , menu (nullptr) + , menu(new Gtk::Menu()) , selected (-1) // -1 means that the button is invalid { button = thisButton; @@ -48,59 +48,148 @@ PopUpCommon::PopUpCommon (Gtk::Button* thisButton, const Glib::ustring& label) setExpandAlignProperties(buttonGroup, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); buttonGroup->attach(*button, 0, 0, 1, 1); buttonGroup->get_style_context()->add_class("image-combo"); + + // Create the image for the button + buttonImage = Gtk::make_managed(); + setExpandAlignProperties(buttonImage, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + imageContainer->attach_next_to(*buttonImage, Gtk::POS_RIGHT, 1, 1); + buttonImage->set_no_show_all(); + + // Create the button for showing the pop-up. + arrowButton = Gtk::make_managed(); + Gtk::Image *arrowImage = Gtk::make_managed(); + arrowImage->set_from_icon_name("pan-down-symbolic", Gtk::ICON_SIZE_BUTTON); + setExpandAlignProperties(arrowButton, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); + arrowButton->add(*arrowImage); //menuSymbol); + arrowImage->show(); + buttonGroup->attach_next_to(*arrowButton, *button, Gtk::POS_RIGHT, 1, 1); + arrowButton->signal_button_release_event().connect_notify(sigc::mem_fun(*this, &PopUpCommon::showMenu)); + arrowButton->get_style_context()->add_class("Right"); + arrowButton->get_style_context()->add_class("popupbutton-arrow"); + arrowButton->set_no_show_all(); } PopUpCommon::~PopUpCommon () { - delete menu; - delete buttonImage; } bool PopUpCommon::addEntry (const Glib::ustring& fileName, const Glib::ustring& label) { - if (label.empty ()) - return false; + return insertEntry(getEntryCount(), fileName, label); +} + +bool PopUpCommon::insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label) +{ + RTImage* image = nullptr; + if (!fileName.empty()) { + image = Gtk::make_managed(fileName); + } + bool success = insertEntryImpl(position, fileName, Glib::RefPtr(), image, label); + if (!success && image) { + delete image; + } + return success; +} + +bool PopUpCommon::insertEntry(int position, const Glib::RefPtr& gIcon, const Glib::ustring& label) +{ + RTImage* image = Gtk::make_managed(gIcon, Gtk::ICON_SIZE_BUTTON); + bool success = insertEntryImpl(position, "", gIcon, image, label); + if (!success) { + delete image; + } + return success; +} + +bool PopUpCommon::insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr& gIcon, RTImage* image, const Glib::ustring& label) +{ + if (label.empty() || position < 0 || position > getEntryCount()) + return false; // Create the menu item and image - MyImageMenuItem* newItem = Gtk::manage (new MyImageMenuItem (label, fileName)); - imageFilenames.push_back (fileName); - images.push_back (newItem->getImage ()); - - if (selected == -1) { - // Create the menu on the first item - menu = new Gtk::Menu (); - // Create the image for the button - buttonImage = new RTImage(fileName); - setExpandAlignProperties(buttonImage, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - // Use the first image by default - imageContainer->attach_next_to(*buttonImage, Gtk::POS_RIGHT, 1, 1); - selected = 0; - } + MyImageMenuItem *newItem = Gtk::make_managed(label, image); + imageIcons.insert(imageIcons.begin() + position, gIcon); + imageFilenames.insert(imageFilenames.begin() + position, fileName); + images.insert(images.begin() + position, newItem->getImage()); // When there is at least 1 choice, we add the arrow button if (images.size() == 1) { - Gtk::Button* arrowButton = Gtk::manage( new Gtk::Button() ); - Gtk::Image *arrowImage = Gtk::manage(new Gtk::Image()); - arrowImage->set_from_icon_name("pan-down-symbolic", Gtk::ICON_SIZE_BUTTON); - setExpandAlignProperties(arrowButton, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); - arrowButton->add(*arrowImage); //menuSymbol); - buttonGroup->attach_next_to(*arrowButton, *button, Gtk::POS_RIGHT, 1, 1); - arrowButton->signal_button_release_event().connect_notify( sigc::mem_fun(*this, &PopUpCommon::showMenu) ); + changeImage(fileName, gIcon); + buttonImage->show(); + selected = 0; button->get_style_context()->add_class("Left"); - arrowButton->get_style_context()->add_class("Right"); - arrowButton->get_style_context()->add_class("popupbutton-arrow"); + arrowButton->show(); hasMenu = true; + } else if (position <= selected) { + selected++; } - newItem->signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &PopUpCommon::entrySelected), images.size () - 1)); - menu->append (*newItem); - + void (PopUpCommon::*entrySelectedFunc)(Gtk::Widget *) = &PopUpCommon::entrySelected; + newItem->signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, entrySelectedFunc), newItem)); + menu->insert(*newItem, position); return true; } -// TODO: 'PopUpCommon::removeEntry' method to be created... +void PopUpCommon::removeEntry(int position) +{ + if (position < 0 || position >= getEntryCount()) { + return; + } -void PopUpCommon::entrySelected (int i) + if (getEntryCount() == 1) { // Last of the entries. + // Hide the arrow button. + button->get_style_context()->remove_class("Left"); + arrowButton->hide(); + hasMenu = false; + // Remove the button image. + buttonImage->hide(); + selected = -1; + } + else if (position < selected) { + selected--; + } + else if (position == selected) { // Select a different entry before removing. + int nextSelection = position + (position == getEntryCount() - 1 ? -1 : 1); + changeImage(nextSelection); + setButtonHint(); + } + + MyImageMenuItem *menuItem = dynamic_cast(menu->get_children()[position]); + menu->remove(*menuItem); + delete menuItem; + imageIcons.erase(imageIcons.begin() + position); + imageFilenames.erase(imageFilenames.begin() + position); + images.erase(images.begin() + position); +} + +void PopUpCommon::changeImage(int position) +{ + changeImage(imageFilenames.at(position), imageIcons.at(position)); +} + +void PopUpCommon::changeImage(const Glib::ustring& fileName, const Glib::RefPtr& gIcon) +{ + if (!fileName.empty()) { + buttonImage->changeImage(fileName); + } else { + buttonImage->changeImage(gIcon, static_cast(Gtk::ICON_SIZE_BUTTON)); + } +} + +void PopUpCommon::entrySelected(Gtk::Widget* widget) +{ + int i = 0; + for (const auto & child : menu->get_children()) { + if (widget == child) { + break; + } + i++; + } + + entrySelected(i); +} + +void PopUpCommon::entrySelected(int i) { // Emit a signal if the selected item has changed if (setSelected (posToIndex(i))) @@ -130,7 +219,7 @@ bool PopUpCommon::setSelected (int entryNum) return false; } else { // Maybe we could do something better than loading the image file each time the selection is changed !? - buttonImage->changeImage(imageFilenames.at(entryNum)); + changeImage(entryNum); selected = entryNum; setButtonHint(); return true; diff --git a/rtgui/popupcommon.h b/rtgui/popupcommon.h index b4cf4d7e0..59a5b8d0e 100644 --- a/rtgui/popupcommon.h +++ b/rtgui/popupcommon.h @@ -20,12 +20,19 @@ */ #pragma once +#include "glibmm/refptr.h" +#include #include #include #include +namespace Gio +{ +class Icon; +} + namespace Gtk { @@ -33,6 +40,7 @@ class Grid; class Menu; class Button; class ImageMenuItem; +class Widget; } @@ -53,9 +61,12 @@ public: explicit PopUpCommon (Gtk::Button* button, const Glib::ustring& label = ""); virtual ~PopUpCommon (); bool addEntry (const Glib::ustring& fileName, const Glib::ustring& label); + bool insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label); + bool insertEntry(int position, const Glib::RefPtr& gIcon, const Glib::ustring& label); int getEntryCount () const; bool setSelected (int entryNum); int getSelected () const; + void removeEntry(int position); void setButtonHint(); void show (); void set_tooltip_text (const Glib::ustring &text); @@ -65,16 +76,22 @@ private: type_signal_changed messageChanged; type_signal_item_selected messageItemSelected; + std::vector> imageIcons; std::vector imageFilenames; std::vector images; Glib::ustring buttonHint; RTImage* buttonImage; Gtk::Grid* imageContainer; - Gtk::Menu* menu; + std::unique_ptr menu; Gtk::Button* button; + Gtk::Button* arrowButton; int selected; bool hasMenu; + void changeImage(int position); + void changeImage(const Glib::ustring& fileName, const Glib::RefPtr& gIcon); + void entrySelected(Gtk::Widget* menuItem); + bool insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr& gIcon, RTImage* image, const Glib::ustring& label); void showMenu(GdkEventButton* event); protected: