mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 14:51:06 +00:00
Design tab: Text dialog with font, height and live outline; offer header
- Text opens a dialog instead of a bare text entry: any installed font
(bold, italic), the height in mm, and a live outline of exactly what
will be inserted with its size. Enter inserts, Esc cancels; the last
font and height are remembered. text_to_regions gains an overload
taking a loaded font.
- The offer menu opens with a greyed title naming what the rows act on
("Flat face 4 of Body 2", "Sketch line", "Nothing selected").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
This commit is contained in:
@@ -60,6 +60,9 @@ pointing at things stays quiet.
|
||||
The offer also opens by itself the moment you press Sketch on a face or plane, showing the
|
||||
sketch tools — the app hands you the tools directly.
|
||||
|
||||
The first line names what the rows act on — *"Flat face 4 of Body 2"*, *"Sketch line"*,
|
||||
*"Nothing selected"* — so the menu is readable even when the selection is not under the cursor.
|
||||
|
||||
**Eight families, always in this fixed order:** Create, Add material, Remove, Dress-up,
|
||||
Repeat, Transform, Reference, Modify.
|
||||
|
||||
@@ -302,6 +305,10 @@ variable and the whole model follows.
|
||||
|
||||
## Import and export
|
||||
|
||||
**Text** asks for the words, the font (any installed font, bold, italic) and the height, and
|
||||
shows the outline that will be inserted with its size in millimetres before anything is added.
|
||||
The last font and height are remembered.
|
||||
|
||||
**Import STEP** brings in a real B-rep solid, not a mesh: its faces and edges can be filleted,
|
||||
shelled and cut like anything modelled here.
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ src/slic3r/GUI/CAD/DesignPanel.cpp
|
||||
src/slic3r/GUI/CAD/DesignSketchTool.cpp
|
||||
src/slic3r/GUI/CAD/SketchInlineEditor.cpp
|
||||
src/slic3r/GUI/CAD/DesignOffer.hpp
|
||||
src/slic3r/GUI/CAD/DesignTextDialog.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
||||
src/slic3r/GUI/KeyChord.cpp
|
||||
|
||||
@@ -67,12 +67,18 @@ ImportRegions text_to_regions(const std::string& utf8, double size_mm,
|
||||
return {};
|
||||
|
||||
const std::string path = font_path.empty() ? default_font_path() : font_path;
|
||||
std::unique_ptr<Emboss::FontFile> ff = Emboss::create_font_file(path.c_str());
|
||||
if (!ff)
|
||||
return {};
|
||||
Emboss::FontFileWithCache fwc(std::move(ff));
|
||||
if (!fwc.has_value())
|
||||
return text_to_regions(utf8, size_mm,
|
||||
std::shared_ptr<const Emboss::FontFile>(Emboss::create_font_file(path.c_str())));
|
||||
}
|
||||
|
||||
ImportRegions text_to_regions(const std::string& utf8, double size_mm,
|
||||
const std::shared_ptr<const Emboss::FontFile>& ff)
|
||||
{
|
||||
if (utf8.empty() || size_mm <= 0.0 || !ff)
|
||||
return {};
|
||||
Emboss::FontFileWithCache fwc;
|
||||
fwc.font_file = ff;
|
||||
fwc.cache = std::make_shared<Emboss::Glyphs>();
|
||||
|
||||
FontProp prop(static_cast<float>(size_mm)); // per_glyph=false
|
||||
HealedExPolygons healed = Emboss::text2shapes(fwc, utf8.c_str(), prop);
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
|
||||
#include "libslic3r/Point.hpp" // Vec2d
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace Emboss { struct FontFile; }
|
||||
|
||||
// A rigid imported region: contour[0] = outer loop, contour[1..] = holes;
|
||||
// points in plane (u,v) millimetres. The nested vector type matches
|
||||
// CadFeature::imported_regions exactly, so results assign directly.
|
||||
@@ -19,6 +22,11 @@ using ImportRegions = std::vector<ImportRegion>;
|
||||
// font (resources/fonts). Returns an empty vector on any failure.
|
||||
ImportRegions text_to_regions(const std::string& utf8, double size_mm,
|
||||
const std::string& font_path = std::string());
|
||||
// Same, from an already loaded font — the GUI resolves a system font (face, bold, italic)
|
||||
// to one and keeps it while the user types. Returns an empty vector when the font is null or
|
||||
// yields no shape.
|
||||
ImportRegions text_to_regions(const std::string& utf8, double size_mm,
|
||||
const std::shared_ptr<const Emboss::FontFile>& font);
|
||||
|
||||
// Parse an SVG file's filled paths into regions (mm), centred on the origin.
|
||||
// `scale` multiplies the authored size (1.0 = as authored). Returns an empty
|
||||
|
||||
@@ -820,6 +820,8 @@ if (SLIC3R_CAD)
|
||||
list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/CAD/DesignPanel.cpp
|
||||
GUI/CAD/DesignPanel.hpp
|
||||
GUI/CAD/DesignTextDialog.cpp
|
||||
GUI/CAD/DesignTextDialog.hpp
|
||||
GUI/CAD/DesignCanvas.cpp
|
||||
GUI/CAD/DesignCanvas.hpp
|
||||
GUI/CAD/DesignSketchTool.cpp
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "slic3r/GUI/CAD/DesignPanel.hpp"
|
||||
#include "slic3r/GUI/CAD/DesignCanvas.hpp"
|
||||
#include "slic3r/GUI/CAD/DesignSketchTool.hpp"
|
||||
#include "slic3r/GUI/CAD/DesignTextDialog.hpp" // Text: font, height, live outline
|
||||
#include "slic3r/GUI/CAD/DesignOffer.hpp" // generated offer table — see docs/ux/tool_atlas.json
|
||||
#include "libslic3r/CAD/GeometryEngine.hpp" // face_by_index for face-extrude gizmo anchor
|
||||
#include "libslic3r/TriangleMesh.hpp" // mesh import: STL/OBJ -> indexed_triangle_set
|
||||
@@ -4562,15 +4563,12 @@ void DesignPanel::sync_sketch_display()
|
||||
|
||||
void DesignPanel::on_add_text()
|
||||
{
|
||||
wxTextEntryDialog dlg(this, _L("Text to insert:"), _L("Text"), wxEmptyString);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
// Font, height and a live outline with its size are chosen up front; the bbox handles
|
||||
// (Move/Scale) still resize the art in the canvas afterwards.
|
||||
DesignTextDialog dlg(this);
|
||||
if (dlg.ShowModal() != wxID_OK || dlg.regions().empty())
|
||||
return;
|
||||
const wxString text = dlg.GetValue();
|
||||
if (text.empty())
|
||||
return;
|
||||
const std::string utf8(text.ToUTF8().data());
|
||||
// Insert at a default height; resize in-canvas via the bbox handles (Move/Scale).
|
||||
add_imported_sketch(text_to_regions(utf8, 10.0), _L("Text"));
|
||||
add_imported_sketch(dlg.regions(), _L("Text"));
|
||||
}
|
||||
|
||||
void DesignPanel::on_import_svg()
|
||||
@@ -6369,6 +6367,38 @@ static void offer_trace(const char* fmt, ...)
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// The offer menu's title line for selection `kind` (an OfferSel), or empty for none.
|
||||
wxString DesignPanel::offer_header(int kind) const
|
||||
{
|
||||
const int nb = int(m_doc.bodies.size());
|
||||
auto body = [&]() {
|
||||
if (m_sel_solid_body < 0 || m_sel_solid_body >= nb) return wxString();
|
||||
const std::string& n = m_doc.bodies[m_sel_solid_body].name;
|
||||
return n.empty() ? wxString::Format(_L("Body %d"), m_sel_solid_body + 1) : wxString::FromUTF8(n);
|
||||
};
|
||||
switch (OfferSel(kind)) {
|
||||
case OfferSel::None: return _L("Nothing selected");
|
||||
case OfferSel::BodySolid: return wxString::Format(_L("%s — solid body"), body());
|
||||
case OfferSel::BodySheet: return wxString::Format(_L("%s — surface body"), body());
|
||||
case OfferSel::FacePlanar: return wxString::Format(_L("Flat face %d of %s"), m_sel_solid_face, body());
|
||||
case OfferSel::FaceCyl: return wxString::Format(_L("Cylindrical face %d of %s"), m_sel_solid_face, body());
|
||||
case OfferSel::FaceOther: return wxString::Format(_L("Face %d of %s"), m_sel_solid_face, body());
|
||||
case OfferSel::EdgeStr: return wxString::Format(_L("Straight edge %d of %s"), m_sel_solid_edge, body());
|
||||
case OfferSel::EdgeCirc: return wxString::Format(_L("Circular edge %d of %s"), m_sel_solid_edge, body());
|
||||
case OfferSel::Vertex: return wxString::Format(_L("Vertex of %s"), body());
|
||||
case OfferSel::SkLoop: return _L("Sketch profile");
|
||||
case OfferSel::SkNone: return _L("Sketch — nothing selected");
|
||||
case OfferSel::SkLine: return _L("Sketch line");
|
||||
case OfferSel::SkArc: return _L("Sketch curve");
|
||||
case OfferSel::SkPoint: return _L("Sketch point");
|
||||
case OfferSel::Sk2Ent: {
|
||||
const int n = m_viewport ? m_viewport->sketch_selection_count() : 0;
|
||||
return wxString::Format(_L("%d sketch entities"), n);
|
||||
}
|
||||
default: return wxString();
|
||||
}
|
||||
}
|
||||
|
||||
void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
||||
{
|
||||
const int kind = offer_selection_kind();
|
||||
@@ -6420,6 +6450,17 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
||||
std::vector<const OfferVerb*> bound; // menu id offset -> verb
|
||||
const int base = wxID_HIGHEST + 4200;
|
||||
|
||||
// Header: WHAT the rows below act on, named the way the tree and the cards name it. The rows
|
||||
// only make sense against the selection, and the selection is not always visible under the
|
||||
// cursor that opened the menu. Greyed, so it reads as a title and cannot be run.
|
||||
{
|
||||
const wxString head = offer_header(kind);
|
||||
if (!head.empty()) {
|
||||
menu.Append(wxID_ANY, head)->Enable(false);
|
||||
menu.AppendSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 0; row < kOfferRowCount; ++row) {
|
||||
std::vector<const OfferVerb*> live, family;
|
||||
for (int i = 0; i < kOfferVerbCount; ++i) {
|
||||
|
||||
@@ -755,6 +755,7 @@ private:
|
||||
// monitor, and the menu would map there — detached from the geometry it is about.
|
||||
wxPoint offer_anchor() const;
|
||||
int offer_selection_kind() const; // an OfferSel, as int to keep the header light
|
||||
wxString offer_header(int kind) const; // the offer menu's title line for that selection
|
||||
// Does the SKETCH half of the map apply? A mode question, not a session one: begin_sketch
|
||||
// does not run until the first tool is armed, so between "press Sketch" and "pick a tool"
|
||||
// is_sketching() is still false — precisely when the drawing tools must be on offer. The
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "DesignTextDialog.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Emboss.hpp"
|
||||
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/fontenum.h>
|
||||
#include <wx/graphics.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
static const char* kFontKey = "cad_text_font"; // WxFontUtils::store_wxFont descriptor
|
||||
static const char* kHeightKey = "cad_text_height"; // mm
|
||||
|
||||
DesignTextDialog::DesignTextDialog(wxWindow* parent)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Text"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
SetBackgroundColour(wxGetApp().dark_mode() ? wxColour(0x2d, 0x2d, 0x31) : *wxWHITE);
|
||||
const int em = em_unit();
|
||||
|
||||
// Last used font and height, else the system GUI font at 10 mm.
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
double height = 10.0;
|
||||
if (AppConfig* cfg = wxGetApp().app_config) {
|
||||
const std::string desc = cfg->get(kFontKey);
|
||||
if (!desc.empty()) {
|
||||
wxFont f = WxFontUtils::load_wxFont(desc);
|
||||
if (f.IsOk()) font = f;
|
||||
}
|
||||
const std::string h = cfg->get(kHeightKey);
|
||||
if (!h.empty()) {
|
||||
try { height = std::clamp(std::stod(h), 0.5, 500.0); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
auto* form = new wxFlexGridSizer(2, em / 2, em);
|
||||
form->AddGrowableCol(1, 1);
|
||||
|
||||
m_text = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(30 * em, -1),
|
||||
wxTE_PROCESS_ENTER);
|
||||
m_text->SetHint(_L("Type the text to insert"));
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Text")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
form->Add(m_text, 1, wxEXPAND);
|
||||
|
||||
// Scalable faces only ('@' faces are the vertical-writing aliases GTK/MSW list twice).
|
||||
wxArrayString faces = wxFontEnumerator::GetFacenames(wxFONTENCODING_SYSTEM, false);
|
||||
faces.erase(std::remove_if(faces.begin(), faces.end(), [](const wxString& f) { return f.StartsWith("@"); }),
|
||||
faces.end());
|
||||
faces.Sort();
|
||||
m_face = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, faces);
|
||||
int sel = m_face->FindString(font.GetFaceName());
|
||||
m_face->SetSelection(sel != wxNOT_FOUND ? sel : 0);
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Font")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
form->Add(m_face, 1, wxEXPAND);
|
||||
|
||||
auto* style = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_bold = new wxCheckBox(this, wxID_ANY, _L("Bold"));
|
||||
m_italic = new wxCheckBox(this, wxID_ANY, _L("Italic"));
|
||||
m_bold->SetValue(WxFontUtils::is_bold(font));
|
||||
m_italic->SetValue(WxFontUtils::is_italic(font));
|
||||
style->Add(m_bold, 0, wxRIGHT, em);
|
||||
style->Add(m_italic, 0);
|
||||
form->AddSpacer(0);
|
||||
form->Add(style, 0);
|
||||
|
||||
m_height = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(10 * em, -1),
|
||||
wxSP_ARROW_KEYS, 0.5, 500.0, height, 0.5);
|
||||
m_height->SetDigits(1);
|
||||
form->Add(new wxStaticText(this, wxID_ANY, _L("Height (mm)")), 0, wxALIGN_CENTER_VERTICAL);
|
||||
form->Add(m_height, 0);
|
||||
|
||||
// The outline of what will be inserted, fitted to the box, with its real size under it.
|
||||
m_preview = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(30 * em, 10 * em));
|
||||
m_preview->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
m_preview->Bind(wxEVT_PAINT, [this](wxPaintEvent&) { draw_preview(m_preview); });
|
||||
m_preview->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_preview->Refresh(); e.Skip(); });
|
||||
m_size = new wxStaticText(this, wxID_ANY, wxEmptyString);
|
||||
|
||||
auto* buttons = new DialogButtons(this, {"OK", "Cancel"});
|
||||
m_ok = buttons->GetOK();
|
||||
|
||||
auto* top = new wxBoxSizer(wxVERTICAL);
|
||||
top->Add(form, 0, wxEXPAND | wxALL, em);
|
||||
top->Add(m_preview, 1, wxEXPAND | wxLEFT | wxRIGHT, em);
|
||||
top->Add(m_size, 0, wxLEFT | wxRIGHT | wxTOP, em);
|
||||
top->Add(buttons, 0, wxEXPAND);
|
||||
|
||||
m_text->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update_preview(); });
|
||||
m_text->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) {
|
||||
if (!m_regions.empty()) EndModal(wxID_OK); // Enter = OK, as everywhere in the tab
|
||||
});
|
||||
auto refont = [this](wxCommandEvent&) { load_font(); update_preview(); };
|
||||
m_face->Bind(wxEVT_CHOICE, refont);
|
||||
m_bold->Bind(wxEVT_CHECKBOX, refont);
|
||||
m_italic->Bind(wxEVT_CHECKBOX, refont);
|
||||
m_height->Bind(wxEVT_SPINCTRLDOUBLE, [this](wxSpinDoubleEvent&) { update_preview(); });
|
||||
m_height->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update_preview(); });
|
||||
Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
|
||||
if (e.GetId() != wxID_OK) { e.Skip(); return; }
|
||||
if (m_regions.empty()) return; // nothing to insert: the size line says why
|
||||
if (AppConfig* cfg = wxGetApp().app_config) {
|
||||
cfg->set(kFontKey, WxFontUtils::store_wxFont(current_font()));
|
||||
cfg->set(kHeightKey, std::to_string(m_height->GetValue()));
|
||||
}
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
SetSizerAndFit(top);
|
||||
CenterOnParent();
|
||||
|
||||
load_font();
|
||||
update_preview();
|
||||
m_text->SetFocus();
|
||||
}
|
||||
|
||||
wxString DesignTextDialog::text() const { return m_text->GetValue(); }
|
||||
|
||||
wxFont DesignTextDialog::current_font() const
|
||||
{
|
||||
wxFontInfo info(12);
|
||||
if (m_face->GetSelection() != wxNOT_FOUND)
|
||||
info.FaceName(m_face->GetStringSelection());
|
||||
info.Bold(m_bold->GetValue()).Italic(m_italic->GetValue());
|
||||
return wxFont(info);
|
||||
}
|
||||
|
||||
void DesignTextDialog::load_font()
|
||||
{
|
||||
const wxFont font = current_font();
|
||||
m_font_file.reset();
|
||||
if (font.IsOk() && WxFontUtils::can_load(font))
|
||||
m_font_file = WxFontUtils::create_font_file(font);
|
||||
}
|
||||
|
||||
void DesignTextDialog::update_preview()
|
||||
{
|
||||
const std::string utf8(m_text->GetValue().ToUTF8().data());
|
||||
m_regions.clear();
|
||||
if (m_font_file && !utf8.empty())
|
||||
m_regions = text_to_regions(utf8, m_height->GetValue(), m_font_file);
|
||||
|
||||
wxString line;
|
||||
if (!m_font_file)
|
||||
line = _L("This font cannot be used for text — pick another one");
|
||||
else if (utf8.empty())
|
||||
line = _L("Type the text to see its size");
|
||||
else if (m_regions.empty())
|
||||
line = _L("This font has no outline for this text");
|
||||
else {
|
||||
double lo_x = std::numeric_limits<double>::max(), lo_y = lo_x;
|
||||
double hi_x = -lo_x, hi_y = -lo_x;
|
||||
for (const auto& r : m_regions)
|
||||
for (const auto& c : r)
|
||||
for (const Vec2d& p : c) {
|
||||
lo_x = std::min(lo_x, p.x()); hi_x = std::max(hi_x, p.x());
|
||||
lo_y = std::min(lo_y, p.y()); hi_y = std::max(hi_y, p.y());
|
||||
}
|
||||
line = wxString::Format(_L("Size: %.1f × %.1f mm"), hi_x - lo_x, hi_y - lo_y);
|
||||
}
|
||||
m_size->SetLabel(line);
|
||||
if (m_ok) m_ok->Enable(!m_regions.empty());
|
||||
m_preview->Refresh();
|
||||
}
|
||||
|
||||
void DesignTextDialog::draw_preview(wxWindow* canvas)
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(canvas);
|
||||
const bool dark = wxGetApp().dark_mode();
|
||||
dc.SetBackground(wxBrush(dark ? wxColour(0x1e, 0x1e, 0x22) : wxColour(0xf4, 0xf4, 0xf6)));
|
||||
dc.Clear();
|
||||
if (m_regions.empty()) return;
|
||||
|
||||
double lo_x = std::numeric_limits<double>::max(), lo_y = lo_x, hi_x = -lo_x, hi_y = -lo_x;
|
||||
for (const auto& r : m_regions)
|
||||
for (const auto& c : r)
|
||||
for (const Vec2d& p : c) {
|
||||
lo_x = std::min(lo_x, p.x()); hi_x = std::max(hi_x, p.x());
|
||||
lo_y = std::min(lo_y, p.y()); hi_y = std::max(hi_y, p.y());
|
||||
}
|
||||
const wxSize sz = canvas->GetClientSize();
|
||||
const double pad = 0.08 * std::min(sz.x, sz.y);
|
||||
const double w = std::max(hi_x - lo_x, 1e-6), h = std::max(hi_y - lo_y, 1e-6);
|
||||
const double k = std::min((sz.x - 2 * pad) / w, (sz.y - 2 * pad) / h);
|
||||
const double ox = 0.5 * (sz.x - k * w), oy = 0.5 * (sz.y + k * h); // y up -> screen y down
|
||||
|
||||
std::unique_ptr<wxGraphicsContext> gc(wxGraphicsContext::Create(dc));
|
||||
if (!gc) return;
|
||||
wxGraphicsPath path = gc->CreatePath();
|
||||
for (const auto& r : m_regions)
|
||||
for (const auto& c : r) {
|
||||
if (c.empty()) continue;
|
||||
path.MoveToPoint(ox + k * (c[0].x() - lo_x), oy - k * (c[0].y() - lo_y));
|
||||
for (size_t i = 1; i < c.size(); ++i)
|
||||
path.AddLineToPoint(ox + k * (c[i].x() - lo_x), oy - k * (c[i].y() - lo_y));
|
||||
path.CloseSubpath();
|
||||
}
|
||||
gc->SetBrush(wxBrush(wxColour(0x00, 0x96, 0x88))); // Orca accent
|
||||
gc->SetPen(*wxTRANSPARENT_PEN);
|
||||
gc->FillPath(path, wxODDEVEN_RULE); // holes (the counter of an "o") stay open
|
||||
}
|
||||
|
||||
void DesignTextDialog::on_dpi_changed(const wxRect&)
|
||||
{
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef slic3r_GUI_DesignTextDialog_hpp_
|
||||
#define slic3r_GUI_DesignTextDialog_hpp_
|
||||
|
||||
#include "slic3r/GUI/GUI_Utils.hpp"
|
||||
#include "libslic3r/CAD/SketchImport.hpp"
|
||||
|
||||
#include <wx/font.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class wxTextCtrl;
|
||||
class wxChoice;
|
||||
class wxCheckBox;
|
||||
class wxSpinCtrlDouble;
|
||||
class wxStaticText;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Emboss { struct FontFile; }
|
||||
namespace GUI {
|
||||
|
||||
// Text for the Design tab: the words, the font (face, bold, italic) and the height, with a
|
||||
// live outline of exactly what will be inserted and its size in millimetres. The outline IS
|
||||
// the result — the same vectorisation the sketch receives — so there is nothing to guess.
|
||||
// Enter inserts, Esc cancels (charter 4.2). The last font and height are remembered.
|
||||
class DesignTextDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
explicit DesignTextDialog(wxWindow* parent);
|
||||
|
||||
// The vectorised text, centred on the origin, in mm. Empty unless the dialog ended wxID_OK.
|
||||
const ImportRegions& regions() const { return m_regions; }
|
||||
wxString text() const;
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
private:
|
||||
void load_font(); // m_face/m_bold/m_italic -> m_font_file
|
||||
void update_preview(); // text/font/height -> m_regions + size label
|
||||
wxFont current_font() const;
|
||||
void draw_preview(wxWindow* canvas);
|
||||
|
||||
wxTextCtrl* m_text{nullptr};
|
||||
wxChoice* m_face{nullptr};
|
||||
wxCheckBox* m_bold{nullptr};
|
||||
wxCheckBox* m_italic{nullptr};
|
||||
wxSpinCtrlDouble* m_height{nullptr};
|
||||
wxWindow* m_preview{nullptr};
|
||||
wxStaticText* m_size{nullptr};
|
||||
wxWindow* m_ok{nullptr};
|
||||
|
||||
std::shared_ptr<const Emboss::FontFile> m_font_file;
|
||||
ImportRegions m_regions;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GUI_DesignTextDialog_hpp_
|
||||
Reference in New Issue
Block a user