Add radio indicator to external editor selector

Make it easier to see that the selector is for changing the current
editor and not for immediately sending the image to the clicked-on
editor.
This commit is contained in:
Lawrence Lee 2023-01-15 15:10:54 -08:00
parent 194ef397a6
commit 22edf5f069
No known key found for this signature in database
GPG Key ID: 048FF2B76A63895F
9 changed files with 186 additions and 56 deletions

View File

@ -1278,6 +1278,11 @@ menuitem:hover > * {
color: @text-hl-color;
}
menu menuitem > radio + * image:not(.dummy),
#MyExpander menu menuitem > radio + * image:not(.dummy) {
margin-left: 1pt;
}
menu image:not(.dummy),
#MyExpander menu image:not(.dummy) {
min-height: 2em;

View File

@ -383,6 +383,11 @@ menu arrow {
margin: 0 -0.25em 0 0;
}
menu menuitem > radio + * image:not(.dummy),
#MyExpander menu menuitem > radio + * image:not(.dummy) {
margin-left: 1pt;
}
menu image:not(.dummy),
#MyExpander menu image:not(.dummy) {
min-height: 2em;
@ -1029,4 +1034,4 @@ messagedialog headerbar button.titlebutton {
min-height: 1.25em;
margin: 0;
}
/*** end ***************************************************************************************/
/*** end ***************************************************************************************/

View File

@ -351,6 +351,11 @@ menu arrow {
margin: 0 -0.25em 0 0;
}
menu menuitem > radio + * image:not(.dummy),
#MyExpander menu menuitem > radio + * image:not(.dummy) {
margin-left: 1pt;
}
menu image:not(.dummy),
#MyExpander menu image:not(.dummy) {
min-height: 2em;

View File

@ -905,7 +905,6 @@ EditorPanel::EditorPanel (FilePanel* filePanel)
send_to_external = Gtk::manage(new PopUpButton("", false));
send_to_external->set_tooltip_text(M("MAIN_BUTTON_SENDTOEDITOR_TOOLTIP"));
setExpandAlignProperties(send_to_external->buttonGroup, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
send_to_external->addEntry("palette-brush.png", M("GENERAL_OTHER"));
updateExternalEditorWidget(
options.externalEditorIndex >= 0 ? options.externalEditorIndex : options.externalEditors.size(),
options.externalEditors
@ -2748,10 +2747,14 @@ void EditorPanel::tbShowHideSidePanels_managestate()
void EditorPanel::updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> &editors)
{
// Remove the editors and leave the "Other" entry.
while (send_to_external->getEntryCount() > 1) {
send_to_external->removeEntry(0);
// Remove the editors.
while (send_to_external->getEntryCount()) {
send_to_external->removeEntry(send_to_external->getEntryCount() - 1);
}
// Create new radio button group because they cannot be reused: https://developer-old.gnome.org/gtkmm/3.16/classGtk_1_1RadioButtonGroup.html#details.
send_to_external_radio_group = Gtk::RadioButtonGroup();
// Add the editors.
for (unsigned i = 0; i < editors.size(); i++) {
const auto & name = editors[i].name.empty() ? Glib::ustring(" ") : editors[i].name;
@ -2771,11 +2774,12 @@ void EditorPanel::updateExternalEditorWidget(int selectedIndex, const std::vecto
gioIcon = Gio::Icon::deserialize(Glib::VariantBase(icon_variant));
}
send_to_external->insertEntry(i, gioIcon, name);
send_to_external->insertEntry(i, gioIcon, name, &send_to_external_radio_group);
} else {
send_to_external->insertEntry(i, "palette-brush.png", name);
send_to_external->insertEntry(i, "palette-brush.png", name, &send_to_external_radio_group);
}
}
send_to_external->addEntry("palette-brush.png", M("GENERAL_OTHER"), &send_to_external_radio_group);
send_to_external->setSelected(selectedIndex);
send_to_external->show();
}

View File

@ -246,6 +246,7 @@ private:
Gtk::Button* queueimg;
Gtk::Button* saveimgas;
PopUpButton* send_to_external;
Gtk::RadioButtonGroup send_to_external_radio_group;
Gtk::Button* navSync;
Gtk::Button* navNext;
Gtk::Button* navPrev;

View File

@ -1506,48 +1506,110 @@ TextOrIcon::TextOrIcon (const Glib::ustring &fname, const Glib::ustring &labelTx
}
MyImageMenuItem::MyImageMenuItem(Glib::ustring label, Glib::ustring imageFileName)
class ImageAndLabel::Impl
{
RTImage* itemImage = nullptr;
public:
RTImage* image;
Gtk::Label* label;
if (!imageFileName.empty()) {
itemImage = Gtk::manage(new RTImage(imageFileName));
Impl(RTImage* image, Gtk::Label* label) : image(image), label(label) {}
static std::unique_ptr<RTImage> createImage(const Glib::ustring& fileName);
};
std::unique_ptr<RTImage> ImageAndLabel::Impl::createImage(const Glib::ustring& fileName)
{
if (fileName.empty()) {
return nullptr;
}
construct(label, itemImage);
return std::unique_ptr<RTImage>(new RTImage(fileName));
}
MyImageMenuItem::MyImageMenuItem(Glib::ustring label, RTImage* itemImage) {
construct(label, itemImage);
ImageAndLabel::ImageAndLabel(const Glib::ustring& label, const Glib::ustring& imageFileName) :
ImageAndLabel(label, Gtk::manage(Impl::createImage(imageFileName).release()))
{
}
void MyImageMenuItem::construct(Glib::ustring label, RTImage* itemImage)
ImageAndLabel::ImageAndLabel(const Glib::ustring& label, RTImage *image) :
pimpl(new Impl(image, Gtk::manage(new Gtk::Label(label))))
{
box = Gtk::manage (new Gtk::Grid());
this->label = Gtk::manage( new Gtk::Label(label));
box->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
Gtk::Grid* grid = Gtk::manage(new Gtk::Grid());
grid->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
if (itemImage) {
image = itemImage;
box->attach_next_to(*image, Gtk::POS_LEFT, 1, 1);
} else {
image = nullptr;
if (image) {
grid->attach_next_to(*image, Gtk::POS_LEFT, 1, 1);
}
box->attach_next_to(*this->label, Gtk::POS_RIGHT, 1, 1);
box->set_column_spacing(4);
box->set_row_spacing(0);
add(*box);
grid->attach_next_to(*(pimpl->label), Gtk::POS_RIGHT, 1, 1);
grid->set_column_spacing(4);
grid->set_row_spacing(0);
pack_start(*grid, Gtk::PACK_SHRINK, 0);
}
const RTImage* ImageAndLabel::getImage() const
{
return pimpl->image;
}
const Gtk::Label* ImageAndLabel::getLabel() const
{
return pimpl->label;
}
class MyImageMenuItem::Impl
{
private:
std::unique_ptr<ImageAndLabel> widget;
public:
Impl(const Glib::ustring &label, const Glib::ustring &imageFileName) :
widget(new ImageAndLabel(label, imageFileName)) {}
Impl(const Glib::ustring &label, RTImage *itemImage) :
widget(new ImageAndLabel(label, itemImage)) {}
ImageAndLabel* getWidget() const { return widget.get(); }
};
MyImageMenuItem::MyImageMenuItem(const Glib::ustring& label, const Glib::ustring& imageFileName) :
pimpl(new Impl(label, imageFileName))
{
add(*(pimpl->getWidget()));
}
MyImageMenuItem::MyImageMenuItem(const Glib::ustring& label, RTImage* itemImage) :
pimpl(new Impl(label, itemImage))
{
add(*(pimpl->getWidget()));
}
const RTImage *MyImageMenuItem::getImage () const
{
return image;
return pimpl->getWidget()->getImage();
}
const Gtk::Label* MyImageMenuItem::getLabel () const
{
return label;
return pimpl->getWidget()->getLabel();
}
class MyRadioImageMenuItem::Impl
{
std::unique_ptr<ImageAndLabel> widget;
public:
Impl(const Glib::ustring &label, RTImage *image) :
widget(new ImageAndLabel(label, image)) {}
ImageAndLabel* getWidget() const { return widget.get(); }
};
MyRadioImageMenuItem::MyRadioImageMenuItem(const Glib::ustring& label, RTImage *image, Gtk::RadioButton::Group& group) :
Gtk::RadioMenuItem(group),
pimpl(new Impl(label, image))
{
add(*(pimpl->getWidget()));
}
const Gtk::Label* MyRadioImageMenuItem::getLabel() const
{
return pimpl->getWidget()->getLabel();
}
MyProgressBar::MyProgressBar(int width) : w(rtengine::max(width, 10 * RTScalable::getScale())) {}

View File

@ -482,20 +482,56 @@ public:
TextOrIcon (const Glib::ustring &filename, const Glib::ustring &labelTx, const Glib::ustring &tooltipTx);
};
class MyImageMenuItem final : public Gtk::MenuItem
/**
* Widget with image and label placed horizontally.
*/
class ImageAndLabel final : public Gtk::Box
{
private:
Gtk::Grid *box;
RTImage *image;
Gtk::Label *label;
void construct(Glib::ustring label, RTImage* image);
class Impl;
std::unique_ptr<Impl> pimpl;
public:
MyImageMenuItem (Glib::ustring label, Glib::ustring imageFileName);
MyImageMenuItem (Glib::ustring label, RTImage* image);
ImageAndLabel(const Glib::ustring& label, const Glib::ustring& imageFileName);
ImageAndLabel(const Glib::ustring& label, RTImage* image);
const RTImage* getImage() const;
const Gtk::Label* getLabel() const;
};
/**
* Menu item with an image and label.
*/
class MyImageMenuItemInterface
{
public:
virtual const Gtk::Label* getLabel() const = 0;
};
/**
* Basic image menu item.
*/
class MyImageMenuItem final : public Gtk::MenuItem, public MyImageMenuItemInterface
{
class Impl;
std::unique_ptr<Impl> pimpl;
public:
MyImageMenuItem (const Glib::ustring& label, const Glib::ustring& imageFileName);
MyImageMenuItem (const Glib::ustring& label, RTImage* image);
const RTImage *getImage () const;
const Gtk::Label* getLabel () const;
const Gtk::Label* getLabel() const override;
};
/**
* Image menu item with radio selector.
*/
class MyRadioImageMenuItem final : public Gtk::RadioMenuItem, public MyImageMenuItemInterface
{
class Impl;
std::unique_ptr<Impl> pimpl;
public:
MyRadioImageMenuItem(const Glib::ustring& label, RTImage* image, Gtk::RadioButton::Group& group);
const Gtk::Label* getLabel() const override;
};
class MyProgressBar final : public Gtk::ProgressBar

View File

@ -73,44 +73,50 @@ PopUpCommon::~PopUpCommon ()
{
}
bool PopUpCommon::addEntry (const Glib::ustring& fileName, const Glib::ustring& label)
bool PopUpCommon::addEntry (const Glib::ustring& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup)
{
return insertEntry(getEntryCount(), fileName, label);
return insertEntry(getEntryCount(), fileName, label, radioGroup);
}
bool PopUpCommon::insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label)
bool PopUpCommon::insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup)
{
RTImage* image = nullptr;
if (!fileName.empty()) {
image = Gtk::manage(new RTImage(fileName));
}
bool success = insertEntryImpl(position, fileName, Glib::RefPtr<const Gio::Icon>(), image, label);
bool success = insertEntryImpl(position, fileName, Glib::RefPtr<const Gio::Icon>(), image, label, radioGroup);
if (!success && image) {
delete image;
}
return success;
}
bool PopUpCommon::insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label)
bool PopUpCommon::insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label, Gtk::RadioButtonGroup *radioGroup)
{
RTImage* image = Gtk::manage(new RTImage(gIcon, Gtk::ICON_SIZE_BUTTON));
bool success = insertEntryImpl(position, "", gIcon, image, label);
bool success = insertEntryImpl(position, "", gIcon, image, label, radioGroup);
if (!success) {
delete image;
}
return success;
}
bool PopUpCommon::insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label)
bool PopUpCommon::insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup)
{
if (label.empty() || position < 0 || position > getEntryCount())
return false;
// Create the menu item and image
MyImageMenuItem *newItem = Gtk::manage(new MyImageMenuItem(label, image));
Gtk::MenuItem *newItem;
if (radioGroup) {
newItem = Gtk::manage(new MyRadioImageMenuItem(label, image, *radioGroup));
}
else {
newItem = Gtk::manage(new MyImageMenuItem(label, image));
}
imageIcons.insert(imageIcons.begin() + position, gIcon);
imageFilenames.insert(imageFilenames.begin() + position, fileName);
images.insert(images.begin() + position, newItem->getImage());
images.insert(images.begin() + position, image);
// When there is at least 1 choice, we add the arrow button
if (images.size() == 1) {
@ -154,9 +160,8 @@ void PopUpCommon::removeEntry(int position)
setButtonHint();
}
MyImageMenuItem *menuItem = dynamic_cast<MyImageMenuItem *>(menu->get_children()[position]);
std::unique_ptr<Gtk::Widget> menuItem(menu->get_children()[position]);
menu->remove(*menuItem);
delete menuItem;
imageIcons.erase(imageIcons.begin() + position);
imageFilenames.erase(imageFilenames.begin() + position);
images.erase(images.begin() + position);
@ -222,6 +227,12 @@ bool PopUpCommon::setSelected (int entryNum)
changeImage(entryNum);
selected = entryNum;
setButtonHint();
auto radioMenuItem = dynamic_cast<Gtk::RadioMenuItem*>(menu->get_children()[entryNum]);
if (radioMenuItem && menu->get_active() != radioMenuItem) {
radioMenuItem->set_active();
}
return true;
}
}
@ -248,7 +259,7 @@ void PopUpCommon::setButtonHint()
if (selected > -1) {
auto widget = menu->get_children ()[selected];
auto item = dynamic_cast<MyImageMenuItem*>(widget);
auto item = dynamic_cast<MyImageMenuItemInterface*>(widget);
if (item) {
hint += escapeHtmlChars(item->getLabel()->get_text());

View File

@ -40,6 +40,7 @@ class Grid;
class Menu;
class Button;
class ImageMenuItem;
class RadioButtonGroup;
class Widget;
}
@ -60,9 +61,9 @@ 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<const Gio::Icon>& gIcon, const Glib::ustring& label);
bool addEntry (const Glib::ustring& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup = nullptr);
bool insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup = nullptr);
bool insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup = nullptr);
int getEntryCount () const;
bool setSelected (int entryNum);
int getSelected () const;
@ -91,7 +92,7 @@ private:
void changeImage(int position);
void changeImage(const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon);
void entrySelected(Gtk::Widget* menuItem);
bool insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label);
bool insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label, Gtk::RadioButtonGroup* radioGroup);
void showMenu(GdkEventButton* event);
protected: