Improvements / Fixes for RadioGroup and apply to more windows II (#10089)

* init

* Fix issue that `Button` is not focusable on Linux

See: https://github.com/SoftFever/OrcaSlicer/pull/10089#issuecomment-3065050902

---------

Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
This commit is contained in:
yw4z
2025-07-12 14:26:47 +03:00
committed by GitHub
parent eba08bf727
commit b410154cb3
7 changed files with 155 additions and 280 deletions

View File

@@ -125,7 +125,7 @@
#include "ParamsDialog.hpp" #include "ParamsDialog.hpp"
#include "Widgets/Label.hpp" #include "Widgets/Label.hpp"
#include "Widgets/RoundedRectangle.hpp" #include "Widgets/RoundedRectangle.hpp"
#include "Widgets/RadioBox.hpp" #include "Widgets/RadioGroup.hpp"
#include "Widgets/CheckBox.hpp" #include "Widgets/CheckBox.hpp"
#include "Widgets/Button.hpp" #include "Widgets/Button.hpp"
@@ -10744,23 +10744,12 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
return true; return true;
} }
class RadioBox;
class RadioSelector
{
public:
int m_select_id;
int m_groupid;
RadioBox *m_radiobox;
};
WX_DECLARE_LIST(RadioSelector, RadioSelectorList);
#define PROJECT_DROP_DIALOG_SELECT_PLANE_SIZE wxSize(FromDIP(350), FromDIP(120)) #define PROJECT_DROP_DIALOG_SELECT_PLANE_SIZE wxSize(FromDIP(350), FromDIP(120))
class ProjectDropDialog : public DPIDialog class ProjectDropDialog : public DPIDialog
{ {
private: private:
wxColour m_def_color = wxColour(255, 255, 255); wxColour m_def_color = wxColour(255, 255, 255);
RadioSelectorList m_radio_group;
int m_action{1}; int m_action{1};
bool m_remember_choice{false}; bool m_remember_choice{false};
@@ -10773,12 +10762,9 @@ public:
wxStaticText *m_fname_s; wxStaticText *m_fname_s;
StaticBox * m_panel_select; StaticBox * m_panel_select;
void select_radio(int index);
void on_select_radio(wxMouseEvent &event);
void on_select_ok(wxCommandEvent &event); void on_select_ok(wxCommandEvent &event);
void on_select_cancel(wxCommandEvent &event); void on_select_cancel(wxCommandEvent &event);
int get_select_radio(int groupid);
int get_action() const { return m_action; } int get_action() const { return m_action; }
void set_action(int index) { m_action = index; } void set_action(int index) { m_action = index; }
@@ -10796,6 +10782,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
wxDefaultPosition, wxDefaultPosition,
wxDefaultSize, wxDefaultSize,
wxCAPTION | wxCLOSE_BOX) wxCAPTION | wxCLOSE_BOX)
, m_action(2)
{ {
// def setting // def setting
SetBackgroundColour(m_def_color); SetBackgroundColour(m_def_color);
@@ -10818,7 +10805,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
m_fname_title = new wxStaticText(this, wxID_ANY, _L("Please select an action"), wxDefaultPosition, wxDefaultSize, 0); m_fname_title = new wxStaticText(this, wxID_ANY, _L("Please select an action"), wxDefaultPosition, wxDefaultSize, 0);
m_fname_title->Wrap(-1); m_fname_title->Wrap(-1);
m_fname_title->SetFont(::Label::Body_13); m_fname_title->SetFont(::Label::Body_14);
m_fname_title->SetForegroundColour(wxColour(107, 107, 107)); m_fname_title->SetForegroundColour(wxColour(107, 107, 107));
m_fname_title->SetBackgroundColour(wxColour(255, 255, 255)); m_fname_title->SetBackgroundColour(wxColour(255, 255, 255));
@@ -10826,7 +10813,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
m_sizer_fline->Add(0, 0, 0, wxEXPAND | wxLEFT, 5); m_sizer_fline->Add(0, 0, 0, wxEXPAND | wxLEFT, 5);
m_fname_f = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); m_fname_f = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
m_fname_f->SetFont(::Label::Head_13); m_fname_f->SetFont(::Label::Head_14);
m_fname_f->Wrap(-1); m_fname_f->Wrap(-1);
m_fname_f->SetForegroundColour(wxColour(38, 46, 48)); m_fname_f->SetForegroundColour(wxColour(38, 46, 48));
@@ -10835,43 +10822,25 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
m_sizer_name->Add(m_sizer_fline, 1, wxEXPAND, 0); m_sizer_name->Add(m_sizer_fline, 1, wxEXPAND, 0);
m_fname_s = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); m_fname_s = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
m_fname_s->SetFont(::Label::Head_13); m_fname_s->SetFont(::Label::Head_14);
m_fname_s->Wrap(-1); m_fname_s->Wrap(-1);
m_fname_s->SetForegroundColour(wxColour(38, 46, 48)); m_fname_s->SetForegroundColour(wxColour(38, 46, 48));
m_sizer_name->Add(m_fname_s, 1, wxALL, 0); m_sizer_name->Add(m_fname_s, 1, wxALL, 0);
m_sizer_main->Add(m_sizer_name, 1, wxEXPAND | wxLEFT | wxRIGHT, 40); m_sizer_main->Add(m_sizer_name, 1, wxEXPAND | wxLEFT | wxRIGHT, 20);
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 5); auto radio_group = new RadioGroup(this, {
_L("Open as project"), // 0
_L("Import geometry only") // 1
}, wxVERTICAL);
radio_group->SetMinSize(wxSize(FromDIP(300),-1));
radio_group->SetSelection(get_action() - 1);
radio_group->Bind(wxEVT_COMMAND_RADIOBOX_SELECTED, [this, radio_group](wxCommandEvent &e) {
set_action(radio_group->GetSelection() + 1);
});
m_panel_select = new StaticBox(this, wxID_ANY, wxDefaultPosition, PROJECT_DROP_DIALOG_SELECT_PLANE_SIZE); m_sizer_main->Add(radio_group, 0, wxEXPAND | wxLEFT | wxRIGHT, 20);
StateColor box_colour(std::pair<wxColour, int>(wxColour("#F8F8F8"), StateColor::Normal));
StateColor box_border_colour(std::pair<wxColour, int>(wxColour(*wxWHITE), StateColor::Normal));
m_panel_select->SetBackgroundColor(box_colour);
m_panel_select->SetBorderColor(box_border_colour);
m_panel_select->SetCornerRadius(5);
wxBoxSizer *m_sizer_select_h = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *m_sizer_select_v = new wxBoxSizer(wxVERTICAL);
auto select_f = create_item_radiobox(_L("Open as project"), m_panel_select, 1, 0);
auto select_s = create_item_radiobox(_L("Import geometry only"), m_panel_select, 2, 0);
//auto select_t = create_item_radiobox(_L("Import presets only"), m_panel_select,3, 0);
m_sizer_select_v->Add(select_f, 0, wxEXPAND, 5);
m_sizer_select_v->Add(select_s, 0, wxEXPAND, 5);
//m_sizer_select_v->Add(select_t, 0, wxEXPAND, 5);
select_radio(2);
m_sizer_select_h->Add(m_sizer_select_v, 0, wxALIGN_CENTER | wxLEFT, 22);
m_panel_select->SetSizer(m_sizer_select_h);
m_panel_select->Layout();
m_sizer_main->Add(m_panel_select, 0, wxEXPAND | wxLEFT | wxRIGHT, 40);
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 10); m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, 10);
@@ -10923,34 +10892,6 @@ ProjectDropDialog::ProjectDropDialog(const std::string &filename)
wxGetApp().UpdateDlgDarkUI(this); wxGetApp().UpdateDlgDarkUI(this);
} }
wxBoxSizer *ProjectDropDialog ::create_item_radiobox(wxString title, wxWindow *parent, int select_id, int groupid)
{
wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
auto radiobox = new RadioBox(parent);
radiobox->SetBackgroundColour(wxColour(248,248,248));
sizer->Add(radiobox, 0, wxALL, 5);
sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, 5);
auto text = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0);
text->Wrap(-1);
text->SetForegroundColour(wxColour(107, 107, 107));
text->SetBackgroundColour(wxColour(248,248,248));
sizer->Add(text, 0, wxALL, 5);
radiobox->Bind(wxEVT_LEFT_DOWN, &ProjectDropDialog::on_select_radio, this);
text->Bind(wxEVT_LEFT_DOWN, [this, radiobox](auto &e) {
e.SetId(radiobox->GetId());
on_select_radio(e);
});
RadioSelector *rs = new RadioSelector;
rs->m_groupid = groupid;
rs->m_radiobox = radiobox;
rs->m_select_id = select_id;
m_radio_group.Append(rs);
return sizer;
}
wxBoxSizer *ProjectDropDialog::create_remember_checkbox(wxString title, wxWindow *parent, wxString tooltip) wxBoxSizer *ProjectDropDialog::create_remember_checkbox(wxString title, wxWindow *parent, wxString tooltip)
{ {
wxBoxSizer *m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL);
@@ -10977,63 +10918,6 @@ wxBoxSizer *ProjectDropDialog::create_remember_checkbox(wxString title, wxWindow
return m_sizer_checkbox; return m_sizer_checkbox;
} }
void ProjectDropDialog::select_radio(int index)
{
m_action = index;
RadioSelectorList::compatibility_iterator it = m_radio_group.GetFirst();
auto groupid = 0;
while (it) {
RadioSelector *rs = it->GetData();
if (rs->m_select_id == index) groupid = rs->m_groupid;
it = it->GetNext();
}
it = m_radio_group.GetFirst();
while (it) {
RadioSelector *rs = it->GetData();
if (rs->m_groupid == groupid && rs->m_select_id == index) rs->m_radiobox->SetValue(true);
if (rs->m_groupid == groupid && rs->m_select_id != index) rs->m_radiobox->SetValue(false);
it = it->GetNext();
}
}
int ProjectDropDialog::get_select_radio(int groupid)
{
RadioSelectorList::compatibility_iterator it = m_radio_group.GetFirst();
while (it) {
RadioSelector *rs = it->GetData();
if (rs->m_groupid == groupid && rs->m_radiobox->GetValue()) { return rs->m_select_id; }
it = it->GetNext();
}
return 0;
}
void ProjectDropDialog::on_select_radio(wxMouseEvent &event)
{
RadioSelectorList::compatibility_iterator it = m_radio_group.GetFirst();
auto groupid = 0;
while (it) {
RadioSelector *rs = it->GetData();
if (rs->m_radiobox->GetId() == event.GetId()) groupid = rs->m_groupid;
it = it->GetNext();
}
it = m_radio_group.GetFirst();
while (it) {
RadioSelector *rs = it->GetData();
if (rs->m_groupid == groupid && rs->m_radiobox->GetId() == event.GetId()) {
set_action(rs->m_select_id);
rs->m_radiobox->SetValue(true);
}
if (rs->m_groupid == groupid && rs->m_radiobox->GetId() != event.GetId()) rs->m_radiobox->SetValue(false);
it = it->GetNext();
}
}
void ProjectDropDialog::on_select_ok(wxCommandEvent &event) void ProjectDropDialog::on_select_ok(wxCommandEvent &event)
{ {
if (m_remember_choice) { if (m_remember_choice) {

View File

@@ -9,8 +9,6 @@
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/wupdlock.h> #include <wx/wupdlock.h>
// BBS: add radio button for project embedded preset logic
#include <wx/radiobut.h>
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
@@ -56,7 +54,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
} }
wxStaticText *label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as"))) % into_u8(tab->title())).str())); wxStaticText *label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as"))) % into_u8(tab->title())).str()));
label_top->SetFont(::Label::Body_13); label_top->SetFont(::Label::Body_14);
label_top->SetForegroundColour(wxColour(38,46,48)); label_top->SetForegroundColour(wxColour(38,46,48));
@@ -105,70 +103,20 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
if (m_type == Preset::TYPE_PRINTER) m_parent->add_info_for_edit_ph_printer(sizer); if (m_type == Preset::TYPE_PRINTER) m_parent->add_info_for_edit_ph_printer(sizer);
// BBS: add project embedded presets logic // ORCA RadioGroup
wxBoxSizer *radio_sizer = new wxBoxSizer(wxHORIZONTAL); m_radio_group = new RadioGroup(m_parent, {
_L("User Preset"), // 0
_L("Preset Inside Project") // 1
}, wxVERTICAL);
wxBoxSizer *m_sizer_left = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_radio_group, 0, wxEXPAND | wxTOP | wxLEFT, BORDER_W);
m_sizer_left->Add(0, 0, 0, wxLEFT, 25); m_radio_group->Bind(wxEVT_COMMAND_RADIOBOX_SELECTED, [this](wxCommandEvent &e) {
m_save_to_project = m_radio_group->GetSelection() == 1;
m_radio_user = new RadioBox(parent); });
m_radio_user->SetBackgroundColour(SAVE_PRESET_DIALOG_DEF_COLOUR);
m_sizer_left->Add(m_radio_user, 0, wxALIGN_CENTER, 0);
m_sizer_left->Add(0, 0, 0, wxLEFT, 10);
auto m_left_text = new wxStaticText(parent, wxID_ANY, _L("User Preset"), wxDefaultPosition, wxDefaultSize, 0);
m_left_text->Wrap(-1);
m_left_text->SetFont(::Label::Body_13);
m_left_text->SetForegroundColour(wxColour(107,107,107));
m_sizer_left->Add(m_left_text, 0, wxALIGN_CENTER, 0);
radio_sizer->Add(m_sizer_left, 1, wxALIGN_CENTER, 5);
wxBoxSizer *m_sizer_right = new wxBoxSizer(wxHORIZONTAL);
m_sizer_right->Add(0, 0, 0, wxLEFT, 15);
m_radio_project = new RadioBox(parent);
m_radio_project->SetBackgroundColour(SAVE_PRESET_DIALOG_DEF_COLOUR);
m_sizer_right->Add(m_radio_project, 0, wxALIGN_CENTER, 0);
m_sizer_right->Add(0, 0, 0, wxLEFT, 10);
auto m_right_text = new wxStaticText(parent, wxID_ANY, _L("Preset Inside Project"), wxDefaultPosition, wxDefaultSize, 0);
m_right_text->SetForegroundColour(wxColour(107,107,107));
m_right_text->SetFont(::Label::Body_13);
m_right_text->Wrap(-1);
m_sizer_right->Add(m_right_text, 0, wxALIGN_CENTER, 0);
radio_sizer->Add(m_sizer_right, 1, wxEXPAND, 5);
sizer->Add(radio_sizer, 0, wxEXPAND | wxTOP, BORDER_W);
auto radio_clicked = [this](wxMouseEvent &e) {
if (m_radio_user->GetId() == e.GetId()) {
m_radio_user->SetValue(true);
m_radio_project->SetValue(false);
m_save_to_project = false;
}
if (m_radio_project->GetId() == e.GetId()) {
m_radio_user->SetValue(false);
m_radio_project->SetValue(true);
m_save_to_project = true;
}
};
m_radio_user->Bind(wxEVT_LEFT_DOWN, radio_clicked);
m_radio_project->Bind(wxEVT_LEFT_DOWN, radio_clicked);
bool is_project_embedded = m_presets->get_edited_preset().is_project_embedded; bool is_project_embedded = m_presets->get_edited_preset().is_project_embedded;
if (is_project_embedded) m_radio_group->SetSelection(is_project_embedded ? 1 : 0);
m_radio_project->SetValue(true);
else
m_radio_user->SetValue(true);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", create item: type" << Preset::get_type_string(m_type) << ", preset " << m_preset_name BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", create item: type" << Preset::get_type_string(m_type) << ", preset " << m_preset_name
<< ", is_project_embedded = " << is_project_embedded; << ", is_project_embedded = " << is_project_embedded;
@@ -240,22 +188,18 @@ void SavePresetDialog::Item::update()
} }
// BBS: add project embedded presets logic // BBS: add project embedded presets logic
if (existing) { if (existing) { // ORCA RadioGroup
if (existing->is_project_embedded) { if (existing->is_project_embedded) {
m_radio_project->SetValue(true); m_radio_group->SetSelection(1);
m_save_to_project = true; m_save_to_project = true;
} else { } else {
m_radio_user->SetValue(true); m_radio_group->SetSelection(0);
m_save_to_project = false; m_save_to_project = false;
} }
m_radio_user->Disable(); m_radio_group->Disable();
m_radio_project->Disable();
} else { } else {
m_radio_user->Enable(); m_radio_group->Enable();
m_radio_project->Enable(); m_radio_group->SetSelection(m_save_to_project ? 1 : 0);
m_radio_user->SetValue(!m_save_to_project);
m_radio_project->SetValue(m_save_to_project);
} }
m_valid_label->SetLabel(info_line); m_valid_label->SetLabel(info_line);

View File

@@ -6,7 +6,7 @@
#include "libslic3r/Preset.hpp" #include "libslic3r/Preset.hpp"
#include "wxExtensions.hpp" #include "wxExtensions.hpp"
#include "GUI_Utils.hpp" #include "GUI_Utils.hpp"
#include "Widgets/RadioBox.hpp" #include "Widgets/RadioGroup.hpp"
#include "Widgets/Button.hpp" #include "Widgets/Button.hpp"
#include "Widgets/RoundedRectangle.hpp" #include "Widgets/RoundedRectangle.hpp"
#include "Widgets/Label.hpp" #include "Widgets/Label.hpp"
@@ -69,9 +69,8 @@ class SavePresetDialog : public DPIDialog
PresetCollection* m_presets {nullptr}; PresetCollection* m_presets {nullptr};
//BBS: add project embedded preset relate logic //BBS: add project embedded preset relate logic
RadioBox * m_radio_user{nullptr};
RadioBox * m_radio_project{nullptr};
bool m_save_to_project {false}; bool m_save_to_project {false};
RadioGroup* m_radio_group; // ORCA
void update(); void update();
}; };

View File

@@ -133,7 +133,10 @@ bool Button::Enable(bool enable)
return result; return result;
} }
void Button::SetCanFocus(bool canFocus) { this->canFocus = canFocus; } void Button::SetCanFocus(bool canFocus) {
StaticBox::SetCanFocus(canFocus);
this->canFocus = canFocus;
}
void Button::SetValue(bool state) void Button::SetValue(bool state)
{ {

View File

@@ -75,6 +75,7 @@ bool LabeledStaticBox::Create(
SetForegroundColour( text_color.colorForStates(state_handler.states())); SetForegroundColour( text_color.colorForStates(state_handler.states()));
SetBorderColor( border_color.colorForStates(state_handler.states())); SetBorderColor( border_color.colorForStates(state_handler.states()));
SetCanFocus(false); SetCanFocus(false);
DisableFocusFromKeyboard();
return true; return true;
} }

View File

@@ -8,14 +8,15 @@ RadioGroup::RadioGroup(
long direction, long direction,
int row_col_limit int row_col_limit
) )
: wxPanel(parent, wxID_ANY) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER) // ensure wxTAB_TRAVERSAL not applied by default
, m_on( this, "radio_on" , 18) , m_on( this, "radio_on" , 18)
, m_off( this, "radio_off" , 18) , m_off( this, "radio_off" , 18)
, m_on_hover( this, "radio_on_hover" , 18) , m_on_hover( this, "radio_on_hover" , 18)
, m_off_hover(this, "radio_off_hover", 18) , m_off_hover(this, "radio_off_hover", 18)
, m_disabled( this, "radio_off_hover", 18) , m_disabled( this, "radio_disabled" , 18)
, m_selectedIndex(0) , m_selectedIndex(0)
, m_focused(false) , m_focused(false)
, m_enabled(true)
{ {
Create(parent, labels, direction, row_col_limit); Create(parent, labels, direction, row_col_limit);
} }
@@ -28,102 +29,103 @@ void RadioGroup::Create(
) )
{ {
m_labels = labels; m_labels = labels;
m_item_count = m_labels.size();
auto bg = parent->GetBackgroundColour(); auto bg = parent->GetBackgroundColour();
this->SetBackgroundColour(bg); this->SetBackgroundColour(bg);
m_text_color = StateColor(
std::pair(wxColour("#6B6A6A"), (int)StateColor::Disabled),
std::pair(wxColour("#363636"), (int)StateColor::Enabled)
);
m_focus_color = StateColor(
std::pair(bg , (int)StateColor::NotFocused),
std::pair(wxColour("#009688"), (int)StateColor::Focused)
);
auto bmp_size = m_on.GetBmpSize(); auto bmp_size = m_on.GetBmpSize();
int item_count = m_labels.size(); int item_limit = row_col_limit < 0 ? 1 : row_col_limit > m_item_count ? m_item_count : row_col_limit;
int item_limit = row_col_limit < 0 ? 1 : row_col_limit > item_count ? item_count : row_col_limit; int count = (int(m_item_count / item_limit) + (m_item_count % item_limit));
int count = (int(item_count / item_limit) + (item_count % item_limit));
int rows = (direction & wxHORIZONTAL) ? item_limit : count; int rows = (direction & wxHORIZONTAL) ? item_limit : count;
int cols = (direction & wxHORIZONTAL) ? count : item_limit; int cols = (direction & wxHORIZONTAL) ? count : item_limit;
wxFlexGridSizer* f_sizer = new wxFlexGridSizer(rows, cols, 0, 0); wxFlexGridSizer* f_sizer = new wxFlexGridSizer(rows, cols, 0, 0);
SetDoubleBuffered(true); SetDoubleBuffered(true);
AcceptsFocusFromKeyboard();
Bind(wxEVT_SET_FOCUS ,([this](wxFocusEvent e) {m_focused = true ;Refresh(); e.Skip();})); for (int i = 0; i < m_item_count; ++i){
Bind(wxEVT_KILL_FOCUS,([this](wxFocusEvent e) {m_focused = false;Refresh(); e.Skip();}));
Bind(wxEVT_PAINT,([this](wxPaintEvent e) {
wxPaintDC dc(this);
dc.Clear();
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#009688")), 1, wxPENSTYLE_SOLID));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(
m_focused ? wxRect(
m_radioButtons[GetSelection()]->GetRect().GetTopLeft() - wxPoint(1, 3),
m_labelButtons[GetSelection()]->GetRect().GetBottomRight() + wxPoint(4, 1)
) : wxRect(0,0,0,0)
);
if (m_focused) // Required to take focus again since Refresh causing lossing focus
SetFocus();
}));
// DPIDialog's uses wxEVT_CHAR_HOOK
Bind(wxEVT_CHAR_HOOK, ([this](wxKeyEvent&e){
int k = e.GetKeyCode();
bool is_next = (k == WXK_DOWN || k == WXK_RIGHT);
bool is_prev = (k == WXK_LEFT || k == WXK_UP);
if(m_focused){
if (is_next) SelectNext();
else if (is_prev) SelectPrevious();
e.Skip(!(is_next || is_prev));
}else{
e.Skip();
}
}));
for (int i = 0; i < item_count; ++i){
auto rb = new wxStaticBitmap(this, wxID_ANY, m_off.bmp(), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxNO_BORDER); auto rb = new wxStaticBitmap(this, wxID_ANY, m_off.bmp(), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxNO_BORDER);
m_radioButtons.push_back(rb); m_radioButtons.push_back(rb);
rb->Bind(wxEVT_LEFT_DOWN ,([this, i](wxMouseEvent e) {OnClick(i) ; e.Skip();})); rb->Bind(wxEVT_LEFT_DOWN ,([this, i](wxMouseEvent e) {SetSelection(i, true) ; e.Skip();}));
rb->Bind(wxEVT_ENTER_WINDOW,([this, i](wxMouseEvent e) {SetRadioIcon(i, true) ; e.Skip();})); rb->Bind(wxEVT_ENTER_WINDOW,([this, i](wxMouseEvent e) {SetRadioIcon(i, true) ; e.Skip();}));
rb->Bind(wxEVT_LEAVE_WINDOW,([this, i](wxMouseEvent e) { rb->Bind(wxEVT_LEAVE_WINDOW,([this, i](wxMouseEvent e) {
// prevent removing hover effect while switching between button and its text // prevent removing hover effect while switching between button and its text
if(wxFindWindowAtPoint(wxGetMousePosition())->GetId() != m_labelButtons[i]->GetId()) auto win = wxFindWindowAtPoint(wxGetMousePosition());
if(!win || win->GetId() != m_labelButtons[i]->GetId())
SetRadioIcon(i, false); SetRadioIcon(i, false);
e.Skip(); e.Skip();
})); }));
auto tx = new wxStaticText(this, wxID_ANY, " " + m_labels[i], wxDefaultPosition, wxDefaultSize); auto tx = new Button(this, m_labels[i]);
tx->SetForegroundColour(wxColour("#363636")); if(i != 0) // one focusable control must exist. wxPanel starts taking focus if there is no focusable control
tx->SetCanFocus(false);
tx->SetPaddingSize(FromDIP(wxSize(5,2)));
tx->SetBackgroundColor(bg);
tx->SetCornerRadius(0);
tx->SetBorderWidth(FromDIP(1));
tx->SetBorderColor(m_focus_color);
tx->SetFont(Label::Body_14); tx->SetFont(Label::Body_14);
m_labelButtons.push_back(tx); tx->SetTextColor(m_text_color);
tx->Bind(wxEVT_LEFT_DOWN ,([this, i](wxMouseEvent e) {OnClick(i) ; e.Skip();})); tx->Bind(wxEVT_BUTTON ,([this, i](wxCommandEvent e) {SetSelection(i, true) ; e.Skip();}));
tx->Bind(wxEVT_ENTER_WINDOW,([this, i](wxMouseEvent e) {SetRadioIcon(i, true) ; e.Skip();})); tx->Bind(wxEVT_ENTER_WINDOW,([this, i](wxMouseEvent e) {SetRadioIcon(i, true) ; e.Skip();}));
tx->Bind(wxEVT_LEAVE_WINDOW,([this, i](wxMouseEvent e) { tx->Bind(wxEVT_LEAVE_WINDOW,([this, i](wxMouseEvent e) {
// prevent removing hover effect while switching between button and its text // prevent removing hover effect while switching between button and its text
if(wxFindWindowAtPoint(wxGetMousePosition())->GetId() != m_radioButtons[i]->GetId()) auto win = wxFindWindowAtPoint(wxGetMousePosition());
if(!win || win->GetId() != m_radioButtons[i]->GetId())
SetRadioIcon(i, false); SetRadioIcon(i, false);
e.Skip(); e.Skip();
})); }));
tx->Bind(wxEVT_CHAR_HOOK, ([this, tx](wxKeyEvent&e){
if(tx->HasFocus()){
int k = e.GetKeyCode();
bool is_next = (k == WXK_DOWN || k == WXK_RIGHT);
bool is_prev = (k == WXK_LEFT || k == WXK_UP);
if (is_next) SelectNext();
else if (is_prev) SelectPrevious();
e.Skip(!(is_next || is_prev));
}else{
e.Skip();
}
}));
m_labelButtons.push_back(tx);
wxBoxSizer* radio_sizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* radio_sizer = new wxBoxSizer(wxHORIZONTAL);
radio_sizer->Add(rb, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 1); radio_sizer->Add(rb, 0, wxALIGN_CENTER_VERTICAL);
radio_sizer->Add(tx, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, this->FromDIP(15)); radio_sizer->Add(tx, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, this->FromDIP(10));
f_sizer->Add(radio_sizer, 0, wxTOP | wxBOTTOM, this->FromDIP(4)); f_sizer->Add(radio_sizer, 0, wxTOP | wxBOTTOM, this->FromDIP(4));
} }
SetSelection(m_selectedIndex); SetSelection(m_selectedIndex);
SetSizer(f_sizer); SetSizer(f_sizer);
} }
void RadioGroup::OnClick(int i) void RadioGroup::SetSelection(int index, bool focus)
{ {
m_focused = true; // prevents 2 time refresh if (index >= 0 && index < m_item_count){
SetSelection(i); int prev_index = m_selectedIndex;
} if(index != prev_index){ // prevent no focusable item on first creation. wxPanel starts taking focus if there is no focusable control
m_labelButtons[index]->SetCanFocus(true);
void RadioGroup::SetSelection(int index) m_labelButtons[prev_index]->SetCanFocus(false);
{ }
if (index >= 0 && index < static_cast<int>(m_labels.size())){ if(focus)
m_labelButtons[index]->SetFocus();
m_selectedIndex = index; m_selectedIndex = index;
for (size_t i = 0; i < m_labels.size(); ++i) for (size_t i = 0; i < m_item_count; ++i)
SetRadioIcon(i, HasFocus() && i == m_selectedIndex); SetRadioIcon(i, m_labelButtons[index]->HasFocus() && i == m_selectedIndex);
wxCommandEvent evt(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId()); wxCommandEvent evt(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId());
evt.SetInt(index); evt.SetInt(index);
evt.SetString(m_labels[index]); evt.SetString(m_labels[index]);
GetEventHandler()->ProcessEvent(evt); GetEventHandler()->ProcessEvent(evt);
Refresh(); // refresh on every change
} }
} }
@@ -134,16 +136,47 @@ int RadioGroup::GetSelection()
void RadioGroup::SelectNext(bool focus) void RadioGroup::SelectNext(bool focus)
{ {
SetSelection(m_selectedIndex + 1 > (m_radioButtons.size() - 1) ? 0 : m_selectedIndex + 1); SetSelection(m_selectedIndex + 1 > (m_radioButtons.size() - 1) ? 0 : m_selectedIndex + 1, focus);
} }
void RadioGroup::SelectPrevious(bool focus) void RadioGroup::SelectPrevious(bool focus)
{ {
SetSelection(m_selectedIndex - 1 < 0 ? (m_radioButtons.size() - 1) : m_selectedIndex - 1); SetSelection(m_selectedIndex - 1 < 0 ? (m_radioButtons.size() - 1) : m_selectedIndex - 1, focus);
} }
void RadioGroup::SetRadioIcon(int i, bool hover) void RadioGroup::SetRadioIcon(int i, bool hover)
{ {
auto icon = m_selectedIndex == i ? (hover ? m_on_hover : m_on) : (hover ? m_off_hover : m_off); auto icon = !m_enabled ? m_disabled : m_selectedIndex == i ? (hover ? m_on_hover : m_on) : (hover ? m_off_hover : m_off);
m_radioButtons[i]->SetBitmap(icon.bmp()); m_radioButtons[i]->SetBitmap(icon.bmp());
}
bool RadioGroup::Enable(bool enable)
{
m_enabled = enable;
bool result = wxPanel::Enable(enable);
if (result) {
for (size_t i = 0; i < m_item_count; ++i){
SetRadioIcon(i, false);
m_labelButtons[i]->Enable(enable); // normally disabling parent should do this but not
}
wxCommandEvent e(EVT_ENABLE_CHANGED);
e.SetEventObject(this);
GetEventHandler()->ProcessEvent(e);
}
return result;
};
// is focused
bool RadioGroup::Disable() {return RadioGroup::Enable(false);};
bool RadioGroup::IsEnabled(){return m_enabled;};
void RadioGroup::SetRadioTooltip(int i, wxString tooltip)
{
m_radioButtons[i]->SetToolTip(tooltip);
m_labelButtons[i]->SetToolTip(tooltip);
} }

View File

@@ -10,6 +10,8 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "Button.hpp"
class RadioGroup : public wxPanel class RadioGroup : public wxPanel
{ {
@@ -32,32 +34,41 @@ public:
int GetSelection(); int GetSelection();
void SetSelection(int index); void SetSelection(int index, bool focus = false);
void SelectNext(bool focus = true); void SelectNext(bool focus = true);
void SelectPrevious(bool focus = true); void SelectPrevious(bool focus = true);
bool Enable(bool enable = true) override;
bool IsEnabled();
bool Disable();
void SetRadioTooltip(int i, wxString tooltip);
private: private:
std::vector<wxString> m_labels; std::vector<wxString> m_labels;
std::vector<wxStaticBitmap*> m_radioButtons; std::vector<wxStaticBitmap*> m_radioButtons;
std::vector<wxStaticText*> m_labelButtons; std::vector<Button*> m_labelButtons;
int m_selectedIndex; int m_selectedIndex;
int m_item_count;
bool m_focused; bool m_focused;
bool m_enabled;
StateColor m_focus_color;
StateColor m_text_color;
ScalableBitmap m_on; ScalableBitmap m_on;
ScalableBitmap m_off; ScalableBitmap m_off;
ScalableBitmap m_on_hover; ScalableBitmap m_on_hover;
ScalableBitmap m_off_hover; ScalableBitmap m_off_hover;
ScalableBitmap m_disabled; ScalableBitmap m_disabled;
void OnClick(int i);
void UpdateFocus(bool focus);
void SetRadioIcon(int i, bool hover); void SetRadioIcon(int i, bool hover);
void OnKeyDown(wxKeyEvent& e);
}; };
#endif // !slic3r_GUI_RADIOGROUP_hpp_ #endif // !slic3r_GUI_RADIOGROUP_hpp_