mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Publish 3MF: support mixed filaments and per-extruder slot selection
- Publish mixed-filament slots as whole units: serialize the filament_mixed_* definition into project_config on import, grow the receiver's parallel arrays in lockstep, and report unappliable definitions as skipped instead of dropping them silently - Per-extruder printer selection: one inner tab per extruder, rows keyed by full "#N" ids; single-extruder receivers collapse variants onto their slot (first applied, rest skipped), multi-extruder receivers override element-wise - New per-slot "Enable" toggle gating what gets published; enabling a mix auto-enables + Full Publishes its components - Mixed page previews: fixed-size ratio bar, ternary triangle (3 components) and Material Ratio vs Model Height graph (gradients), always visible regardless of Enable - Tab strip shows full swatch compositions with adjustable spacing; barycentric helpers shared via FilamentBitmapUtils
This commit is contained in:
@@ -11,6 +11,45 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Barycentric utilities for a ternary (triangle) ratio picker.
|
||||
double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c)
|
||||
{
|
||||
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
|
||||
}
|
||||
|
||||
bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
|
||||
{
|
||||
double total = tri_signed_area2(v0, v1, v2);
|
||||
if (std::abs(total) < 1e-9) return false;
|
||||
double s0 = tri_signed_area2(p, v1, v2) / total;
|
||||
double s1 = tri_signed_area2(v0, p, v2) / total;
|
||||
double s2 = 1.0 - s0 - s1;
|
||||
return s0 >= -0.001 && s1 >= -0.001 && s2 >= -0.001;
|
||||
}
|
||||
|
||||
void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
|
||||
double& w0, double& w1, double& w2)
|
||||
{
|
||||
double total = std::abs(tri_signed_area2(v0, v1, v2));
|
||||
if (total < 1e-9) { w0 = w1 = w2 = 1.0 / 3.0; return; }
|
||||
w0 = std::abs(tri_signed_area2(p, v1, v2)) / total;
|
||||
w1 = std::abs(tri_signed_area2(v0, p, v2)) / total;
|
||||
w2 = 1.0 - w0 - w1;
|
||||
w0 = std::clamp(w0, 0.0, 1.0);
|
||||
w1 = std::clamp(w1, 0.0, 1.0);
|
||||
w2 = std::clamp(w2, 0.0, 1.0);
|
||||
double s = w0 + w1 + w2;
|
||||
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
|
||||
}
|
||||
|
||||
TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
|
||||
{
|
||||
double w0, w1, w2;
|
||||
tri_barycentric(p, v0, v1, v2, w0, w1, w2);
|
||||
return {w0 * v0.x + w1 * v1.x + w2 * v2.x,
|
||||
w0 * v0.y + w1 * v1.y + w2 * v2.y};
|
||||
}
|
||||
|
||||
void fill_gradient_rect_east(wxDC& dc, const wxRect& rect, const wxColour& from, const wxColour& to)
|
||||
{
|
||||
if (rect.width <= 0 || rect.height <= 0) return;
|
||||
@@ -73,7 +112,7 @@ std::vector<wxColour> sample_gradient_ramp(const wxColour& first,
|
||||
// Resolve the curve a gradient slot is sampled with, mirroring the slicer's fallback in
|
||||
// ToolOrdering: a custom curve wins, otherwise a straight line between gradient_range's
|
||||
// endpoints, otherwise the 0.10 -> 0.90 default.
|
||||
static Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot)
|
||||
Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot)
|
||||
{
|
||||
const auto* curve_opt = cfg.option<ConfigOptionStrings>("filament_mixed_gradient_curve");
|
||||
if (curve_opt && slot < curve_opt->values.size() && !curve_opt->values[slot].empty()) {
|
||||
|
||||
@@ -13,6 +13,16 @@ namespace Slic3r { class DynamicPrintConfig; struct GradientCurve; }
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Barycentric utilities for a ternary (triangle) ratio picker, shared by the mixed-filament
|
||||
// editor and the Publish dialog's read-only definition preview.
|
||||
struct TriPoint { double x, y; };
|
||||
|
||||
double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c);
|
||||
bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2);
|
||||
void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
|
||||
double& w0, double& w1, double& w2);
|
||||
TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2);
|
||||
|
||||
// Fills a rect with a west->east linear gradient by drawing solid 1px columns.
|
||||
// Use instead of wxDC::GradientFillLinear, whose CoreGraphics (CGShading) backend
|
||||
// fails to render on some macOS builds; solid fills are unaffected.
|
||||
@@ -51,6 +61,12 @@ std::vector<wxColour> sample_gradient_ramp(const wxColour& first,
|
||||
// destination's height in pixels.
|
||||
std::vector<wxColour> mixed_gradient_ramp(const Slic3r::DynamicPrintConfig& cfg, size_t slot, int steps);
|
||||
|
||||
// Resolve the curve a gradient slot is sampled with: the custom curve wins when it has at
|
||||
// least two points, otherwise a straight line between gradient_range's endpoints, otherwise
|
||||
// the 0.10 -> 0.90 default. Mirrors the slicer's ToolOrdering fallback so every preview
|
||||
// agrees with what gets sliced. Always returns a two-point curve.
|
||||
Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot);
|
||||
|
||||
// Fill rect with a ramp, ramp.front() along the bottom edge.
|
||||
void fill_gradient_ramp_rect(wxDC& dc, const wxRect& rect, const std::vector<wxColour>& ramp);
|
||||
|
||||
|
||||
@@ -919,52 +919,8 @@ wxBoxSizer* MixedFilamentDialog::create_ratio_slider()
|
||||
}
|
||||
|
||||
// ---- Triangle (ternary) ratio picker ----
|
||||
|
||||
// Barycentric coordinate utilities
|
||||
struct TriPoint { double x, y; };
|
||||
|
||||
static double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c)
|
||||
{
|
||||
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
|
||||
}
|
||||
|
||||
static bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
|
||||
{
|
||||
double total = tri_signed_area2(v0, v1, v2);
|
||||
if (std::abs(total) < 1e-9) return false;
|
||||
double s0 = tri_signed_area2(p, v1, v2) / total;
|
||||
double s1 = tri_signed_area2(v0, p, v2) / total;
|
||||
double s2 = 1.0 - s0 - s1;
|
||||
return s0 >= -0.001 && s1 >= -0.001 && s2 >= -0.001;
|
||||
}
|
||||
|
||||
static void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
|
||||
double& w0, double& w1, double& w2)
|
||||
{
|
||||
double total = std::abs(tri_signed_area2(v0, v1, v2));
|
||||
if (total < 1e-9) { w0 = w1 = w2 = 1.0 / 3.0; return; }
|
||||
w0 = std::abs(tri_signed_area2(p, v1, v2)) / total;
|
||||
w1 = std::abs(tri_signed_area2(v0, p, v2)) / total;
|
||||
w2 = 1.0 - w0 - w1;
|
||||
w0 = std::clamp(w0, 0.0, 1.0);
|
||||
w1 = std::clamp(w1, 0.0, 1.0);
|
||||
w2 = std::clamp(w2, 0.0, 1.0);
|
||||
double s = w0 + w1 + w2;
|
||||
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
|
||||
}
|
||||
|
||||
static TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
|
||||
{
|
||||
double w0, w1, w2;
|
||||
tri_barycentric(p, v0, v1, v2, w0, w1, w2);
|
||||
w0 = std::clamp(w0, 0.0, 1.0);
|
||||
w1 = std::clamp(w1, 0.0, 1.0);
|
||||
w2 = std::clamp(w2, 0.0, 1.0);
|
||||
double s = w0 + w1 + w2;
|
||||
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
|
||||
return {w0 * v0.x + w1 * v1.x + w2 * v2.x,
|
||||
w0 * v0.y + w1 * v1.y + w2 * v2.y};
|
||||
}
|
||||
// The barycentric utilities (TriPoint, tri_contains, tri_barycentric, tri_clamp) live in
|
||||
// FilamentBitmapUtils so the Publish dialog can mirror this picker read-only.
|
||||
|
||||
wxBoxSizer* MixedFilamentDialog::create_triangle_picker()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,10 @@
|
||||
#include "libslic3r/PublishSettings.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/menu.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@@ -105,9 +107,18 @@ private:
|
||||
wxPoint scroll_pos{0, 0};
|
||||
wxStaticBitmap* filament_color_chip{nullptr};
|
||||
wxStaticText* title_label{nullptr}; // material title (static text; Full Publish carries the label elsewhere)
|
||||
// "Enable": while unchecked nothing of this slot is exported and everything below the
|
||||
// header row is hidden. For physical slots the Full Publish toggle sits on a second
|
||||
// line (full_line_item) visible only when enabled; for mixed slots Enable alone implies
|
||||
// publishing the mix definition, so no Full Publish widget exists at all.
|
||||
wxCheckBox* enable_check{nullptr};
|
||||
wxSizerItem* full_line_item{nullptr}; // sizer item of the Full Publish line (physical slots only)
|
||||
// "Full Publish": while checked, the whole slot preset is serialized and its rows
|
||||
// (incl. Color/Type) are disabled.
|
||||
wxCheckBox* full_check{nullptr};
|
||||
// True for a mixed-color filament slot: no Material/Retraction rows; Enable publishes
|
||||
// the slot's gradient/ratio definition as a whole.
|
||||
bool is_mixed{false};
|
||||
// Material identity, only for Section::Material categories.
|
||||
std::string filament_type;
|
||||
std::string filament_vendor;
|
||||
@@ -118,6 +129,21 @@ private:
|
||||
std::vector<size_t> rows; // flattened rows of this category
|
||||
};
|
||||
|
||||
// Frozen snapshot of a mixed filament slot's definition for the read-only visualization
|
||||
// painted on the slot's page. Plain data only: the paint handler must never touch the
|
||||
// config. For gradient slots the curve is pre-sampled (t, ratio) pairs, where ratio is the
|
||||
// first component's share over model height; anchors carry the raw control points.
|
||||
struct MixedVisualSpec
|
||||
{
|
||||
bool valid{false};
|
||||
bool is_gradient{false};
|
||||
std::vector<wxColour> component_colours; // colour per component, in config order
|
||||
std::vector<double> ratios; // sublayer shares summing to ~1 (non-gradient)
|
||||
std::vector<double> tri_weights; // 3-component mixes: barycentric shares
|
||||
std::vector<std::pair<double, double>> gradient_samples;
|
||||
std::vector<std::pair<double, double>> gradient_anchors;
|
||||
};
|
||||
|
||||
// One outer TabCtrl page. Category entries are its inner tabs.
|
||||
struct SectionGroup
|
||||
{
|
||||
@@ -127,13 +153,24 @@ private:
|
||||
ScalableBitmap icon_bmp; // tab icon next to the title; rescaled on DPI change
|
||||
wxPanel* page{nullptr};
|
||||
TabCtrl* tabs{nullptr};
|
||||
// Second tab strip, below the main one, listing only the mixed-color filament slots.
|
||||
// Present on the Material section only (null elsewhere).
|
||||
TabCtrl* mixed_tabs{nullptr};
|
||||
wxPanel* page_host{nullptr};
|
||||
wxBoxSizer* page_host_sizer{nullptr};
|
||||
int selected_inner{-1};
|
||||
std::vector<size_t> categories; // indices into m_categories
|
||||
// Selected mixed tab (index into mixed_categories), valid while a mixed slot page is shown.
|
||||
int selected_mixed{-1};
|
||||
std::vector<size_t> categories; // indices into m_categories (physical slots)
|
||||
std::vector<size_t> mixed_categories; // indices into m_categories (mixed slots)
|
||||
};
|
||||
|
||||
void build_option_model();
|
||||
// Frozen snapshot of a mixed slot's definition for the page visualization, resolved from
|
||||
// the full config once at dialog-build time. Gradient slots pre-sample exactly what the
|
||||
// slicer will print: the custom curve wins over the gradient_range endpoints over the
|
||||
// 0.10 -> 0.90 default (the resolution FilamentBitmapUtils::mixed_gradient_curve mirrors).
|
||||
static MixedVisualSpec make_mixed_visual_spec(const Slic3r::DynamicPrintConfig& full, size_t slot);
|
||||
void apply_filter(const wxString& filter_text);
|
||||
// Menu-only pseudo filters: show only the checked ("Filter selected") or only the
|
||||
// unchecked ("Filter non-selected") rows. The search box keeps the user's text.
|
||||
@@ -147,13 +184,21 @@ private:
|
||||
void set_row_bold(Row& row, bool bold);
|
||||
// "Full Publish" toggled: disables/enables the material's rows.
|
||||
void on_full_toggle(size_t category_index);
|
||||
// "Enable" toggled on a material slot: reveals/hides everything below the header and, for a
|
||||
// mixed slot, auto-selects its component filaments' "Enable" + "Full Publish" toggles.
|
||||
void on_enable_toggle(size_t category_index);
|
||||
// Read-only visualization of a mixed slot's definition (a stacked ratio bar, or the
|
||||
// Material Ratio vs Model Height graph for a gradient), inserted above the info hint
|
||||
// inside the category's scroll area.
|
||||
void add_mixed_visual(size_t category_index, const MixedVisualSpec& spec);
|
||||
// Return/create the fixed outer page for a Section kind.
|
||||
size_t section_group_for(Section kind);
|
||||
size_t category_index_for(const wxString& title,
|
||||
Section section,
|
||||
size_t group,
|
||||
size_t source_index,
|
||||
const PublishMaterialIdentity& identity = PublishMaterialIdentity());
|
||||
const PublishMaterialIdentity& identity = PublishMaterialIdentity(),
|
||||
bool is_mixed = false);
|
||||
size_t subcategory_index_for(size_t category_index, const wxString& title, const wxString& icon);
|
||||
void add_row_ui(const std::string& key,
|
||||
const wxString& label,
|
||||
@@ -167,8 +212,10 @@ private:
|
||||
void save_scroll_position(Category& category);
|
||||
void show_outer_page(size_t section_index);
|
||||
void show_inner_page(size_t section_index, int inner_index);
|
||||
void show_mixed_page(size_t section_index, int mixed_index);
|
||||
void on_outer_tab_changed(wxCommandEvent& event);
|
||||
void on_inner_tab_changed(size_t section_index, wxCommandEvent& event);
|
||||
void on_mixed_tab_changed(size_t section_index, wxCommandEvent& event);
|
||||
bool row_is_visible(const Row& row) const;
|
||||
void apply_visibility();
|
||||
void bind_tab_events();
|
||||
@@ -190,6 +237,7 @@ private:
|
||||
wxString m_info_nonsel;
|
||||
wxString m_info_allsel;
|
||||
wxString m_info_empty;
|
||||
wxString m_info_mix; // body hint shown for a mixed slot (published as a whole)
|
||||
|
||||
ScalableBitmap m_search;
|
||||
ScalableBitmap m_menu;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include <wx/dc.h>
|
||||
|
||||
wxDEFINE_EVENT( wxEVT_TAB_SEL_CHANGING, wxCommandEvent );
|
||||
wxDEFINE_EVENT( wxEVT_TAB_SEL_CHANGED, wxCommandEvent );
|
||||
wxDEFINE_EVENT(wxEVT_TAB_SEL_CHANGING, wxCommandEvent);
|
||||
wxDEFINE_EVENT(wxEVT_TAB_SEL_CHANGED, wxCommandEvent);
|
||||
|
||||
BEGIN_EVENT_TABLE(TabCtrl, StaticBox)
|
||||
|
||||
@@ -22,11 +22,7 @@ END_EVENT_TABLE()
|
||||
#define TAB_BUTTON_PADDING_Y 2
|
||||
#define TAB_BUTTON_PADDING TAB_BUTTON_PADDING_X, TAB_BUTTON_PADDING_Y
|
||||
|
||||
TabCtrl::TabCtrl(wxWindow * parent,
|
||||
wxWindowID id,
|
||||
const wxPoint & pos,
|
||||
const wxSize & size,
|
||||
long style)
|
||||
TabCtrl::TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
|
||||
: StaticBox(parent, id, pos, size, style)
|
||||
{
|
||||
#if 0
|
||||
@@ -42,14 +38,11 @@ TabCtrl::TabCtrl(wxWindow * parent,
|
||||
hsizer->Add(sizer, 0, wxEXPAND | wxBOTTOM, border_width * 4);
|
||||
SetSizer(hsizer);
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &TabCtrl::buttonClicked, this);
|
||||
//wxString reason;
|
||||
//IsTransparentBackgroundSupported(&reason);
|
||||
// wxString reason;
|
||||
// IsTransparentBackgroundSupported(&reason);
|
||||
}
|
||||
|
||||
TabCtrl::~TabCtrl()
|
||||
{
|
||||
delete images;
|
||||
}
|
||||
TabCtrl::~TabCtrl() { delete images; }
|
||||
|
||||
int TabCtrl::GetSelection() const { return sel; }
|
||||
|
||||
@@ -75,14 +68,11 @@ void TabCtrl::SelectItem(int item)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void TabCtrl::Unselect()
|
||||
{
|
||||
SelectItem(-1);
|
||||
}
|
||||
void TabCtrl::Unselect() { SelectItem(-1); }
|
||||
|
||||
void TabCtrl::Rescale()
|
||||
{
|
||||
for (auto & b : btns)
|
||||
for (auto& b : btns)
|
||||
b->Rescale();
|
||||
relayout();
|
||||
}
|
||||
@@ -96,23 +86,20 @@ bool TabCtrl::SetFont(wxFont const& font)
|
||||
return true;
|
||||
}
|
||||
|
||||
int TabCtrl::AppendItem(const wxString &item,
|
||||
int image, int selImage,
|
||||
void * clientData)
|
||||
int TabCtrl::AppendItem(const wxString& item, int image, int selImage, void* clientData)
|
||||
{
|
||||
Button * btn = new Button();
|
||||
Button* btn = new Button();
|
||||
btn->Create(this, item, "", wxBORDER_NONE);
|
||||
btn->SetFont(GetFont());
|
||||
btn->SetTextColor(StateColor(
|
||||
std::make_pair(0x6B6B6C, (int) StateColor::NotChecked),
|
||||
std::make_pair(*wxLIGHT_GREY, (int) StateColor::Normal)));
|
||||
btn->SetTextColor(
|
||||
StateColor(std::make_pair(0x6B6B6C, (int) StateColor::NotChecked), std::make_pair(*wxLIGHT_GREY, (int) StateColor::Normal)));
|
||||
btn->SetBackgroundColor(StateColor());
|
||||
btn->SetCornerRadius(0);
|
||||
btn->SetPaddingSize({TAB_BUTTON_PADDING});
|
||||
btns.push_back(btn);
|
||||
if (btns.size() > 1)
|
||||
sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0});
|
||||
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, TAB_BUTTON_SPACE * 2);
|
||||
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, item_space * 2);
|
||||
sizer->AddStretchSpacer(1);
|
||||
relayout();
|
||||
return btns.size() - 1;
|
||||
@@ -144,7 +131,7 @@ bool TabCtrl::DeleteItem(int item)
|
||||
sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0});
|
||||
|
||||
if (selection_changed) {
|
||||
sel--; // `relayout()` uses `sel` so we need to update this before calling `relayout()`
|
||||
sel--; // `relayout()` uses `sel` so we need to update this before calling `relayout()`
|
||||
}
|
||||
relayout();
|
||||
if (selection_changed) {
|
||||
@@ -167,14 +154,12 @@ void TabCtrl::DeleteAllItems()
|
||||
|
||||
unsigned int TabCtrl::GetCount() const { return btns.size(); }
|
||||
|
||||
wxString TabCtrl::GetItemText(unsigned int item) const
|
||||
{
|
||||
return item < btns.size() ? btns[item]->GetLabel() : wxString{};
|
||||
}
|
||||
wxString TabCtrl::GetItemText(unsigned int item) const { return item < btns.size() ? btns[item]->GetLabel() : wxString{}; }
|
||||
|
||||
void TabCtrl::SetItemText(unsigned int item, wxString const &value)
|
||||
void TabCtrl::SetItemText(unsigned int item, wxString const& value)
|
||||
{
|
||||
if (item >= btns.size()) return;
|
||||
if (item >= btns.size())
|
||||
return;
|
||||
btns[item]->SetLabel(value);
|
||||
}
|
||||
|
||||
@@ -188,61 +173,59 @@ void TabCtrl::SetItemBitmap(unsigned int item, const wxBitmap& bitmap)
|
||||
|
||||
bool TabCtrl::GetItemBold(unsigned int item) const
|
||||
{
|
||||
if (item >= btns.size()) return false;
|
||||
if (item >= btns.size())
|
||||
return false;
|
||||
return btns[item]->GetFont() == bold;
|
||||
}
|
||||
|
||||
void TabCtrl::SetItemBold(unsigned int item, bool bold)
|
||||
{
|
||||
if (item >= btns.size()) return;
|
||||
if (item >= btns.size())
|
||||
return;
|
||||
btns[item]->SetFont(bold ? this->bold : GetFont());
|
||||
btns[item]->Rescale();
|
||||
}
|
||||
|
||||
void* TabCtrl::GetItemData(unsigned int item) const
|
||||
{
|
||||
if (item >= btns.size()) return nullptr;
|
||||
if (item >= btns.size())
|
||||
return nullptr;
|
||||
return btns[item]->GetClientData();
|
||||
}
|
||||
|
||||
void TabCtrl::SetItemData(unsigned int item, void* clientData)
|
||||
{
|
||||
if (item >= btns.size()) return;
|
||||
if (item >= btns.size())
|
||||
return;
|
||||
btns[item]->SetClientData(clientData);
|
||||
}
|
||||
|
||||
void TabCtrl::AssignImageList(wxImageList* imageList)
|
||||
{
|
||||
if (images == imageList) return;
|
||||
if (images == imageList)
|
||||
return;
|
||||
delete images;
|
||||
images = imageList;
|
||||
}
|
||||
|
||||
void TabCtrl::SetItemTextColour(unsigned int item, const StateColor &col)
|
||||
void TabCtrl::SetItemTextColour(unsigned int item, const StateColor& col)
|
||||
{
|
||||
if (item >= btns.size()) return;
|
||||
if (item >= btns.size())
|
||||
return;
|
||||
btns[item]->SetTextColor(col);
|
||||
}
|
||||
|
||||
int TabCtrl::GetFirstVisibleItem() const
|
||||
{
|
||||
return btns.size() == 0 ? -1 : 0;
|
||||
}
|
||||
int TabCtrl::GetFirstVisibleItem() const { return btns.size() == 0 ? -1 : 0; }
|
||||
|
||||
int TabCtrl::GetNextVisible(int item) const
|
||||
{
|
||||
return ++item < btns.size() ? item : -1;
|
||||
}
|
||||
int TabCtrl::GetNextVisible(int item) const { return ++item < btns.size() ? item : -1; }
|
||||
|
||||
bool TabCtrl::IsVisible(unsigned int item) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool TabCtrl::IsVisible(unsigned int item) const { return true; }
|
||||
|
||||
void TabCtrl::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
||||
{
|
||||
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
||||
if (sizeFlags & wxSIZE_USE_EXISTING) return;
|
||||
if (sizeFlags & wxSIZE_USE_EXISTING)
|
||||
return;
|
||||
relayout();
|
||||
}
|
||||
|
||||
@@ -250,7 +233,9 @@ void TabCtrl::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
||||
|
||||
WXLRESULT TabCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
|
||||
{
|
||||
if (nMsg == WM_GETDLGCODE) { return DLGC_WANTARROWS; }
|
||||
if (nMsg == WM_GETDLGCODE) {
|
||||
return DLGC_WANTARROWS;
|
||||
}
|
||||
return wxWindow::MSWWindowProc(nMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
@@ -259,15 +244,15 @@ WXLRESULT TabCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
|
||||
void TabCtrl::relayout()
|
||||
{
|
||||
int offset = 10;
|
||||
int item = sel + 1;
|
||||
int first = 0;
|
||||
int item = sel + 1;
|
||||
int first = 0;
|
||||
for (int i = 0; i < item; ++i)
|
||||
offset += btns[i]->GetMinSize().x + TAB_BUTTON_SPACE * 2;
|
||||
offset += btns[i]->GetMinSize().x + item_space * 2;
|
||||
if (item < btns.size())
|
||||
offset += btns[item]->GetMinSize().x + TAB_BUTTON_SPACE * 2;
|
||||
int width = GetSize().x;
|
||||
offset += btns[item]->GetMinSize().x + item_space * 2;
|
||||
int width = GetSize().x;
|
||||
for (int i = 0; i < btns.size(); ++i) {
|
||||
auto size = btns[i]->GetMinSize().x + TAB_BUTTON_SPACE * 2;
|
||||
auto size = btns[i]->GetMinSize().x + item_space * 2;
|
||||
if (i < sel && offset > width) {
|
||||
sizer->Show(i * 2 + 1, false);
|
||||
sizer->Show(i * 2 + 2, false);
|
||||
@@ -288,23 +273,32 @@ void TabCtrl::relayout()
|
||||
sizer->GetItem(i * 2 + 2)->SetMinSize({0, 0});
|
||||
}
|
||||
if (item >= btns.size())
|
||||
-- item;
|
||||
--item;
|
||||
// Keep spacing 2 ~ 10 TAB_BUTTON_SPACE
|
||||
int b = GetSize().x - offset - 10 - (item + 1 - first) * TAB_BUTTON_SPACE * 8;
|
||||
int b = GetSize().x - offset - 10 - (item + 1 - first) * item_space * 8;
|
||||
sizer->GetItem(item * 2 + 2)->SetMinSize({b > 0 ? b : 0, 0});
|
||||
Layout();
|
||||
}
|
||||
|
||||
int TabCtrl::buttons_best_width() const
|
||||
void TabCtrl::SetItemSpace(int space)
|
||||
{
|
||||
if (space < 0 || space == item_space)
|
||||
return;
|
||||
item_space = space;
|
||||
relayout();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
int TabCtrl::GetFullSize() const
|
||||
{
|
||||
// Mirrors relayout(): a 10px leading spacer plus every button's min width and spacing.
|
||||
int width = 10;
|
||||
for (const Button *btn : btns)
|
||||
width += btn->GetMinSize().x + TAB_BUTTON_SPACE * 2;
|
||||
for (const Button* btn : btns)
|
||||
width += btn->GetMinSize().x + item_space * 2;
|
||||
return width;
|
||||
}
|
||||
|
||||
void TabCtrl::buttonClicked(wxCommandEvent &event)
|
||||
void TabCtrl::buttonClicked(wxCommandEvent& event)
|
||||
{
|
||||
SetFocus();
|
||||
auto btn = event.GetEventObject();
|
||||
@@ -312,7 +306,7 @@ void TabCtrl::buttonClicked(wxCommandEvent &event)
|
||||
SelectItem(iter == btns.end() ? -1 : iter - btns.begin());
|
||||
}
|
||||
|
||||
void TabCtrl::keyDown(wxKeyEvent &event)
|
||||
void TabCtrl::keyDown(wxKeyEvent& event)
|
||||
{
|
||||
switch (event.GetKeyCode()) {
|
||||
case WXK_UP:
|
||||
@@ -331,11 +325,13 @@ void TabCtrl::keyDown(wxKeyEvent &event)
|
||||
void TabCtrl::doRender(wxDC& dc)
|
||||
{
|
||||
wxSize size = GetSize();
|
||||
int states = state_handler.states();
|
||||
if (sel < 0) { return; }
|
||||
int states = state_handler.states();
|
||||
if (sel < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto x1 = btns[sel]->GetPosition().x;
|
||||
auto x2 = x1 + btns[sel]->GetSize().x;
|
||||
auto x1 = btns[sel]->GetPosition().x;
|
||||
auto x2 = x1 + btns[sel]->GetSize().x;
|
||||
const int BS2 = (1 + border_width) / 2;
|
||||
#if 0
|
||||
const int BS = border_width / 2;
|
||||
|
||||
@@ -3,33 +3,30 @@
|
||||
|
||||
#include "Button.hpp"
|
||||
|
||||
wxDECLARE_EVENT( wxEVT_TAB_SEL_CHANGING, wxCommandEvent );
|
||||
wxDECLARE_EVENT( wxEVT_TAB_SEL_CHANGED, wxCommandEvent );
|
||||
wxDECLARE_EVENT(wxEVT_TAB_SEL_CHANGING, wxCommandEvent);
|
||||
wxDECLARE_EVENT(wxEVT_TAB_SEL_CHANGED, wxCommandEvent);
|
||||
|
||||
class TabCtrl : public StaticBox
|
||||
{
|
||||
std::vector<Button*> btns;
|
||||
wxImageList* images = nullptr;
|
||||
wxBoxSizer * sizer = nullptr;
|
||||
wxBoxSizer* sizer = nullptr;
|
||||
|
||||
int sel = -1;
|
||||
wxFont bold;
|
||||
int item_space = 2; // space around each button, both sides (SetItemSpace)
|
||||
|
||||
public:
|
||||
TabCtrl(wxWindow * parent,
|
||||
wxWindowID id,
|
||||
const wxPoint & pos = wxDefaultPosition,
|
||||
const wxSize & size = wxDefaultSize,
|
||||
long style = 0);
|
||||
TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
|
||||
|
||||
~TabCtrl();
|
||||
|
||||
public:
|
||||
virtual bool SetFont(wxFont const & font) override;
|
||||
virtual bool SetFont(wxFont const& font) override;
|
||||
|
||||
public:
|
||||
int AppendItem(const wxString &item, int image = -1, int selImage = -1, void *clientData = nullptr);
|
||||
int AppendItem(const wxString &item, const wxBitmap& bitmap, void *clientData = nullptr);
|
||||
int AppendItem(const wxString& item, int image = -1, int selImage = -1, void* clientData = nullptr);
|
||||
int AppendItem(const wxString& item, const wxBitmap& bitmap, void* clientData = nullptr);
|
||||
|
||||
bool DeleteItem(int item);
|
||||
|
||||
@@ -37,7 +34,7 @@ public:
|
||||
|
||||
unsigned int GetCount() const;
|
||||
|
||||
int GetSelection() const;
|
||||
int GetSelection() const;
|
||||
|
||||
void SelectItem(int item);
|
||||
|
||||
@@ -46,16 +43,16 @@ public:
|
||||
virtual void Rescale();
|
||||
|
||||
wxString GetItemText(unsigned int item) const;
|
||||
void SetItemText(unsigned int item, wxString const &value);
|
||||
void SetItemBitmap(unsigned int item, const wxBitmap& bitmap);
|
||||
void SetItemText(unsigned int item, wxString const& value);
|
||||
void SetItemBitmap(unsigned int item, const wxBitmap& bitmap);
|
||||
|
||||
bool GetItemBold(unsigned int item) const;
|
||||
void SetItemBold(unsigned int item, bool bold);
|
||||
bool GetItemBold(unsigned int item) const;
|
||||
void SetItemBold(unsigned int item, bool bold);
|
||||
|
||||
void* GetItemData(unsigned int item) const;
|
||||
void SetItemData(unsigned int item, void *clientData);
|
||||
|
||||
void AssignImageList(wxImageList *imageList);
|
||||
void* GetItemData(unsigned int item) const;
|
||||
void SetItemData(unsigned int item, void* clientData);
|
||||
|
||||
void AssignImageList(wxImageList* imageList);
|
||||
|
||||
void SetItemTextColour(unsigned int item, const StateColor& col);
|
||||
|
||||
@@ -64,8 +61,11 @@ public:
|
||||
int GetNextVisible(int item) const;
|
||||
bool IsVisible(unsigned int item) const;
|
||||
|
||||
// Width of the tab strip that keeps every button visible (used to size the Publish dialog).
|
||||
int buttons_best_width() const;
|
||||
// Extra space around each tab button (in px on both sides). Defaults to the control-wide
|
||||
// standard; call before appending items so every button picks it up.
|
||||
void SetItemSpace(int space);
|
||||
|
||||
int GetFullSize() const;
|
||||
|
||||
private:
|
||||
virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags = wxSIZE_AUTO) override;
|
||||
@@ -76,10 +76,10 @@ private:
|
||||
|
||||
void relayout();
|
||||
|
||||
void buttonClicked(wxCommandEvent & event);
|
||||
void keyDown(wxKeyEvent &event);
|
||||
void buttonClicked(wxCommandEvent& event);
|
||||
void keyDown(wxKeyEvent& event);
|
||||
|
||||
void doRender(wxDC & dc) override;
|
||||
void doRender(wxDC& dc) override;
|
||||
|
||||
// some useful events
|
||||
bool sendTabCtrlEvent(bool changing = false);
|
||||
|
||||
Reference in New Issue
Block a user