diff --git a/src/libslic3r/GCode/DataType.h b/src/libslic3r/GCode/DataType.h index 2fdcaf2..1c8a443 100644 --- a/src/libslic3r/GCode/DataType.h +++ b/src/libslic3r/GCode/DataType.h @@ -71,9 +71,9 @@ typedef unsigned int UINT32; typedef int INT; #endif -#ifndef INT32 -typedef int INT32; -#endif +// #ifndef INT32 +// typedef int INT32; +// #endif typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ typedef signed char INT8S; /* Signed 8 bit quantity */ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3b3d018..3934a4d 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -20,6 +20,12 @@ set(SLIC3R_GUI_SOURCES GUI/Widgets/StateColor.hpp GUI/Widgets/WebView.cpp GUI/Widgets/WebView.hpp + GUI/Widgets/Label.cpp + GUI/Widgets/Label.hpp + GUI/Widgets/StateHandler.cpp + GUI/Widgets/StateHandler.hpp + GUI/Widgets/StaticBox.cpp + GUI/Widgets/StaticBox.hpp GUI/ConfigSnapshotDialog.cpp GUI/ConfigSnapshotDialog.hpp GUI/3DScene.cpp diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3afc160..51efa92 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -285,9 +285,9 @@ private: // credits infornation credits = title + " " + - //B11 - " "; - // _L("is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n" + + // B11 + _L("is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n"; + //+ // _L("Developed by QIDI Research.") + "\n\n" + // title + " " + _L("is licensed under the") + " " + _L("GNU Affero General Public License, version 3") + ".\n\n" + // _L("Contributions by Vojtech Bubnik, Enrico Turri, Oleksandra Iushchenko, Tamas Meszaros, Lukas Matena, Vojtech Kral, David Kocik and numerous others.") + "\n\n" + diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 360379a..ea615e8 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -59,6 +59,7 @@ namespace Slic3r { namespace GUI { wxDEFINE_EVENT(EVT_LOAD_URL, wxCommandEvent); wxDEFINE_EVENT(EVT_LOAD_PRINTER_URL, wxCommandEvent); +int count = 0; enum class ERescaleTarget { Mainframe, @@ -859,7 +860,12 @@ void MainFrame::create_preset_tabs() //B4 m_printer_view = new PrinterWebView(m_tabpanel); m_printer_view->Hide(); - dynamic_cast(m_tabpanel)->AddPage(m_printer_view, _L("Device"), "tab_monitor_active"); +#ifdef _MSW_DARK_MODE + if (!wxGetApp().tabs_as_menu()) + dynamic_cast(m_tabpanel)->AddPage(m_printer_view, _L("Device"), "tab_monitor_active"); + else +#endif + m_tabpanel->AddPage(m_printer_view, _L("Device")); //B28 m_guide_view = new GuideWebView(m_tabpanel); wxString url = wxString::Format("file://%s/web/guide/index.html", from_u8(resources_dir())); @@ -868,7 +874,12 @@ void MainFrame::create_preset_tabs() url = wxString::Format("file://%s/web/guide/index.html?lang=%s", from_u8(resources_dir()), strlang); m_guide_view->load_url(url); m_guide_view->Hide(); - dynamic_cast(m_tabpanel)->AddPage(m_guide_view, _L("Guide"), "userguide"); +#ifdef _MSW_DARK_MODE + if (!wxGetApp().tabs_as_menu()) + dynamic_cast(m_tabpanel)->AddPage(m_guide_view, _L("Guide"), "userguide"); + else +#endif + m_tabpanel->AddPage(m_guide_view, _L("Guide")); } void MainFrame::add_created_tab(Tab* panel, const std::string& bmp_name /*= ""*/) @@ -2074,8 +2085,11 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/) m_printer_view->load_url(url); } } - // if (m_tabpanel->GetSelection() != (int)new_selection) - // m_tabpanel->SetSelection(new_selection); + if (count == 0 && m_tabpanel->GetSelection() != (int)new_selection) + { + m_tabpanel->SetSelection(new_selection); + count++; + } #ifdef _MSW_DARK_MODE if (wxGetApp().tabs_as_menu()) { if (Tab* cur_tab = dynamic_cast(m_tabpanel->GetPage(new_selection))) diff --git a/src/slic3r/GUI/Widgets/Label.cpp b/src/slic3r/GUI/Widgets/Label.cpp new file mode 100644 index 0000000..8d23033 --- /dev/null +++ b/src/slic3r/GUI/Widgets/Label.cpp @@ -0,0 +1,288 @@ +#include "libslic3r/Utils.hpp" +#include "Label.hpp" +#include "StaticBox.hpp" + +wxFont Label::sysFont(int size, bool bold) +{ +//#ifdef __linux__ +// return wxFont{}; +//#endif +#ifndef __APPLE__ + size = size * 4 / 5; +#endif + + auto face = wxString::FromUTF8("HarmonyOS Sans SC"); + wxFont font{size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, false, face}; + font.SetFaceName(face); + if (!font.IsOk()) { + font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + if (bold) font.MakeBold(); + font.SetPointSize(size); + } + return font; +} +wxFont Label::Head_24; +wxFont Label::Head_20; +wxFont Label::Head_18; +wxFont Label::Head_16; +wxFont Label::Head_15; +wxFont Label::Head_14; +wxFont Label::Head_13; +wxFont Label::Head_12; +wxFont Label::Head_11; +wxFont Label::Head_10; + +wxFont Label::Body_16; +wxFont Label::Body_15; +wxFont Label::Body_14; +wxFont Label::Body_13; +wxFont Label::Body_12; +wxFont Label::Body_11; +wxFont Label::Body_10; +wxFont Label::Body_9; + +void Label::initSysFont() +{ +#ifdef __linux__ + const std::string& resource_path = Slic3r::resources_dir(); + wxString font_path = wxString::FromUTF8(resource_path+"/fonts/HarmonyOS_Sans_SC_Bold.ttf"); + bool result = wxFont::AddPrivateFont(font_path); + //BOOST_LOG_TRIVIAL(info) << boost::format("add font of HarmonyOS_Sans_SC_Bold returns %1%")%result; + printf("add font of HarmonyOS_Sans_SC_Bold returns %d\n", result); + font_path = wxString::FromUTF8(resource_path+"/fonts/HarmonyOS_Sans_SC_Regular.ttf"); + result = wxFont::AddPrivateFont(font_path); + //BOOST_LOG_TRIVIAL(info) << boost::format("add font of HarmonyOS_Sans_SC_Regular returns %1%")%result; + printf("add font of HarmonyOS_Sans_SC_Regular returns %d\n", result); +#endif + + Head_24 = Label::sysFont(24, true); + Head_20 = Label::sysFont(20, true); + Head_18 = Label::sysFont(18, true); + Head_16 = Label::sysFont(16, true); + Head_15 = Label::sysFont(15, true); + Head_14 = Label::sysFont(14, true); + Head_13 = Label::sysFont(13, true); + Head_12 = Label::sysFont(12, true); + Head_11 = Label::sysFont(11, true); + Head_10 = Label::sysFont(10, true); + + Body_16 = Label::sysFont(16, false); + Body_15 = Label::sysFont(15, false); + Body_14 = Label::sysFont(14, false); + Body_13 = Label::sysFont(13, false); + Body_12 = Label::sysFont(12, false); + Body_11 = Label::sysFont(11, false); + Body_10 = Label::sysFont(10, false); + Body_9 = Label::sysFont(9, false); +} + +class WXDLLIMPEXP_CORE wxTextWrapper2 +{ +public: + wxTextWrapper2() { m_eol = false; } + + // win is used for getting the font, text is the text to wrap, width is the + // max line width or -1 to disable wrapping + void Wrap(wxWindow *win, const wxString &text, int widthMax) + { + const wxClientDC dc(win); + + const wxArrayString ls = wxSplit(text, '\n', '\0'); + for (wxArrayString::const_iterator i = ls.begin(); i != ls.end(); ++i) { + wxString line = *i; + + if (i != ls.begin()) { + // Do this even if the line is empty, except if it's the first one. + OnNewLine(); + } + + // Is this a special case when wrapping is disabled? + if (widthMax < 0) { + DoOutputLine(line); + continue; + } + + for (bool newLine = false; !line.empty(); newLine = true) { + if (newLine) OnNewLine(); + + wxArrayInt widths; + dc.GetPartialTextExtents(line, widths); + + const size_t posEnd = std::lower_bound(widths.begin(), widths.end(), widthMax) - widths.begin(); + + // Does the entire remaining line fit? + if (posEnd == line.length()) { + DoOutputLine(line); + break; + } + + // Find the last word to chop off. + size_t lastSpace = posEnd; + while (lastSpace > 0) { + auto c = line[lastSpace]; + if (c == ' ') + break; + if (c > 0x4E00) { + if (lastSpace != posEnd) + ++lastSpace; + break; + } + --lastSpace; + } + if (lastSpace == 0) { + // No spaces, so can't wrap. + lastSpace = posEnd; + } + + // Output the part that fits. + DoOutputLine(line.substr(0, lastSpace)); + + // And redo the layout with the rest. + if (line[lastSpace] == ' ') ++lastSpace; + line = line.substr(lastSpace); + } + } + } + + // we don't need it, but just to avoid compiler warnings + virtual ~wxTextWrapper2() {} + +protected: + // line may be empty + virtual void OnOutputLine(const wxString &line) = 0; + + // called at the start of every new line (except the very first one) + virtual void OnNewLine() {} + +private: + // call OnOutputLine() and set m_eol to true + void DoOutputLine(const wxString &line) + { + OnOutputLine(line); + + m_eol = true; + } + + // this function is a destructive inspector: when it returns true it also + // resets the flag to false so calling it again wouldn't return true any + // more + bool IsStartOfNewLine() + { + if (!m_eol) return false; + + m_eol = false; + + return true; + } + + bool m_eol; +}; + +class wxLabelWrapper2 : public wxTextWrapper2 +{ +public: + void WrapLabel(wxWindow *text, int widthMax) + { + m_text.clear(); + Wrap(text, text->GetLabel(), widthMax); + text->SetLabel(m_text); + } + +protected: + virtual void OnOutputLine(const wxString &line) wxOVERRIDE { m_text += line; } + + virtual void OnNewLine() wxOVERRIDE { m_text += wxT('\n'); } + +private: + wxString m_text; +}; + + +wxSize Label::split_lines(wxDC &dc, int width, const wxString &text, wxString &multiline_text) +{ + multiline_text = text; + if (width > 0 && dc.GetTextExtent(text).x > width) { + size_t start = 0; + while (true) { + size_t idx = size_t(-1); + for (size_t i = start; i < multiline_text.Len(); i++) { + if (multiline_text[i] == ' ') { + if (dc.GetTextExtent(multiline_text.SubString(start, i)).x < width) + idx = i; + else { + if (idx == size_t(-1)) idx = i; + break; + } + } + } + if (idx == size_t(-1)) break; + multiline_text[idx] = '\n'; + start = idx + 1; + if (dc.GetTextExtent(multiline_text.Mid(start)).x < width) break; + } + } + return dc.GetMultiLineTextExtent(multiline_text); +} + +Label::Label(wxWindow *parent, wxString const &text, long style) : Label(parent, Body_14, text, style) {} + +Label::Label(wxWindow *parent, wxFont const &font, wxString const &text, long style) + : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, style) +{ + this->font = font; + SetFont(font); + SetForegroundColour(wxColour("#262E30")); + SetBackgroundColour(StaticBox::GetParentBackgroundColor(parent)); + SetForegroundColour("#262E30"); + if (style & LB_PROPAGATE_MOUSE_EVENT) { + for (auto evt : { + wxEVT_LEFT_UP, wxEVT_LEFT_DOWN}) + Bind(evt, [this] (auto & e) { GetParent()->GetEventHandler()->ProcessEventLocally(e); }); + }; + } + +void Label::SetLabel(const wxString& label) +{ + if (GetLabel() == label) + return; + wxStaticText::SetLabel(label); +#ifdef __WXOSX__ + if ((GetWindowStyle() & LB_HYPERLINK)) { + SetLabelMarkup(label); + return; + } +#endif +} + +void Label::SetWindowStyleFlag(long style) +{ + if (style == GetWindowStyle()) + return; + wxStaticText::SetWindowStyleFlag(style); + if (style & LB_HYPERLINK) { + this->color = GetForegroundColour(); + static wxColor clr_url("#00AE42"); + SetFont(this->font.Underlined()); + SetForegroundColour(clr_url); + SetCursor(wxCURSOR_HAND); +#ifdef __WXOSX__ + SetLabelMarkup(GetLabel()); +#endif + } else { + SetForegroundColour(this->color); + SetFont(this->font); + SetCursor(wxCURSOR_ARROW); +#ifdef __WXOSX__ + auto label = GetLabel(); + wxStaticText::SetLabel({}); + wxStaticText::SetLabel(label); +#endif + } + Refresh(); +} + +void Label::Wrap(int width) +{ + wxLabelWrapper2 wrapper; + wrapper.WrapLabel(this, width); +} diff --git a/src/slic3r/GUI/Widgets/Label.hpp b/src/slic3r/GUI/Widgets/Label.hpp new file mode 100644 index 0000000..e13faf5 --- /dev/null +++ b/src/slic3r/GUI/Widgets/Label.hpp @@ -0,0 +1,55 @@ +#ifndef slic3r_GUI_Label_hpp_ +#define slic3r_GUI_Label_hpp_ + +#include + +#define LB_HYPERLINK 0x0020 +#define LB_PROPAGATE_MOUSE_EVENT 0x0040 + + +class Label : public wxStaticText +{ +public: + Label(wxWindow *parent, wxString const &text = {}, long style = 0); + + Label(wxWindow *parent, wxFont const &font, wxString const &text = {}, long style = 0); + + void SetLabel(const wxString& label) override; + + void SetWindowStyleFlag(long style) override; + + void Wrap(int width); + +private: + wxFont font; + wxColour color; + +public: + static wxFont Head_24; + static wxFont Head_20; + static wxFont Head_18; + static wxFont Head_16; + static wxFont Head_15; + static wxFont Head_14; + static wxFont Head_13; + static wxFont Head_12; + static wxFont Head_11; + static wxFont Head_10; + + static wxFont Body_16; + static wxFont Body_15; + static wxFont Body_14; + static wxFont Body_13; + static wxFont Body_12; + static wxFont Body_10; + static wxFont Body_11; + static wxFont Body_9; + + static void initSysFont(); + + static wxFont sysFont(int size, bool bold = false); + + static wxSize split_lines(wxDC &dc, int width, const wxString &text, wxString &multiline_text); +}; + +#endif // !slic3r_GUI_Label_hpp_ diff --git a/src/slic3r/GUI/Widgets/StateHandler.cpp b/src/slic3r/GUI/Widgets/StateHandler.cpp new file mode 100644 index 0000000..f66585f --- /dev/null +++ b/src/slic3r/GUI/Widgets/StateHandler.cpp @@ -0,0 +1,135 @@ +#include "StateHandler.hpp" + +wxDEFINE_EVENT(EVT_ENABLE_CHANGED, wxCommandEvent); + +StateHandler::StateHandler(wxWindow * owner) + : owner_(owner) +{ + owner_->PushEventHandler(this); + if (owner->IsEnabled()) + states_ |= Enabled; + if (owner->HasFocus()) + states_ |= Focused; +} + +StateHandler::~StateHandler() { owner_->RemoveEventHandler(this); } + +void StateHandler::attach(StateColor const &color) +{ + colors_.push_back(&color); +} + +void StateHandler::attach(std::vector const & colors) +{ + colors_.insert(colors_.end(), colors.begin(), colors.end()); +} + +void StateHandler::attach_child(wxWindow *child) +{ + auto ch = new StateHandler(this, child); + children_.emplace_back(ch); + ch->update_binds(); + states2_ |= ch->states(); +} + +void StateHandler::remove_child(wxWindow *child) +{ + children_.erase(std::remove_if(children_.begin(), children_.end(), + [child](auto &c) { return c->owner_ == child; }), children_.end()); + states2_ = 0; + for (auto & c : children_) states2_ |= c->states(); +} + +void StateHandler::update_binds() +{ + int bind_states = parent_ ? (parent_->bind_states_ & ~Enabled) : 0; + for (auto c : colors_) { + bind_states |= c->states(); + } + bind_states = bind_states | (bind_states >> 16); + int diff = bind_states ^ bind_states_; + State states[] = {Enabled, Checked, Focused, Hovered, Pressed}; + wxEventType events[] = {EVT_ENABLE_CHANGED, wxEVT_CHECKBOX, wxEVT_SET_FOCUS, wxEVT_ENTER_WINDOW, wxEVT_LEFT_DOWN}; + wxEventType events2[] = {{0}, {0}, wxEVT_KILL_FOCUS, wxEVT_LEAVE_WINDOW, wxEVT_LEFT_UP}; + for (int i = 0; i < 5; ++i) { + int s = states[i]; + if (diff & s) { + if (bind_states & s) { + Bind(events[i], &StateHandler::changed, this); + if (events2[i]) + Bind(events2[i], &StateHandler::changed, this); + } else { + Unbind(events[i], &StateHandler::changed, this); + if (events2[i]) + owner_->Unbind(events2[i], &StateHandler::changed, this); + } + } + } + bind_states_ = bind_states; + for (auto &c : children_) c->update_binds(); +} + +void StateHandler::set_state(int state, int mask) +{ + if (states_ & mask == state & mask) return; + int old = states_; + states_ = states_ & ~mask | state & mask; + if (old != states_ && (old | states2_) != (states_ | states2_)) { + if (parent_) + parent_->changed(states_ | states2_); + else + owner_->Refresh(); + } +} + +StateHandler::StateHandler(StateHandler *parent, wxWindow *owner) + : StateHandler(owner) +{ + states_ &= ~Enabled; + parent_ = parent; +} + +void StateHandler::changed(wxEvent &event) +{ + event.Skip(); + wxEventType events[] = {EVT_ENABLE_CHANGED, wxEVT_CHECKBOX, wxEVT_SET_FOCUS, wxEVT_ENTER_WINDOW, wxEVT_LEFT_DOWN}; + wxEventType events2[] = {{0}, {0}, wxEVT_KILL_FOCUS, wxEVT_LEAVE_WINDOW, wxEVT_LEFT_UP}; + int old = states_; + // some events are from another window (ex: text_ctrl of TextInput), save state in states2_ to avoid conflicts + for (int i = 0; i < 5; ++i) { + if (events2[i]) { + if (event.GetEventType() == events[i]) { + states_ |= 1 << i; + break; + } else if (event.GetEventType() == events2[i]) { + states_ &= ~(1 << i); + break; + } + } + else { + if (event.GetEventType() == events[i]) { + states_ ^= (1 << i); + break; + } + } + } + if (old != states_ && (old | states2_) != (states_ | states2_)) { + if (parent_) + parent_->changed(states_ | states2_); + else + owner_->Refresh(); + } +} + +void StateHandler::changed(int) +{ + int old = states2_; + states2_ = 0; + for (auto &c : children_) states2_ |= c->states(); + if (old != states2_ && (old | states_) != (states_ | states2_)) { + if (parent_) + parent_->changed(states_ | states2_); + else + owner_->Refresh(); + } +} diff --git a/src/slic3r/GUI/Widgets/StateHandler.hpp b/src/slic3r/GUI/Widgets/StateHandler.hpp new file mode 100644 index 0000000..c231b8f --- /dev/null +++ b/src/slic3r/GUI/Widgets/StateHandler.hpp @@ -0,0 +1,63 @@ +#ifndef slic3r_GUI_StateHandler_hpp_ +#define slic3r_GUI_StateHandler_hpp_ + +#include + +#include "StateColor.hpp" + +wxDECLARE_EVENT(EVT_ENABLE_CHANGED, wxCommandEvent); + +class StateHandler : public wxEvtHandler +{ +public: + enum State { + Enabled = 1, + Checked = 2, + Focused = 4, + Hovered = 8, + Pressed = 16, + Disabled = 1 << 16, + NotChecked = 2 << 16, + NotFocused = 4 << 16, + NotHovered = 8 << 16, + NotPressed = 16 << 16, + }; + +public: + StateHandler(wxWindow * owner); + + ~StateHandler(); + +public: + void attach(StateColor const & color); + + void attach(std::vector const & colors); + + void attach_child(wxWindow *child); + + void remove_child(wxWindow *child); + + void update_binds(); + + int states() const { return states_ | states2_; } + + void set_state(int state, int mask); + +private: + StateHandler(StateHandler * parent, wxWindow *owner); + + void changed(wxEvent &event); + + void changed(int state2); + +private: + wxWindow * owner_; + std::vector colors_; + int bind_states_ = 0; + int states_ = 0; + int states2_ = 0; // from children + std::vector> children_; + StateHandler * parent_ = nullptr; +}; + +#endif // !slic3r_GUI_StateHandler_hpp_ diff --git a/src/slic3r/GUI/Widgets/StaticBox.cpp b/src/slic3r/GUI/Widgets/StaticBox.cpp new file mode 100644 index 0000000..6fde9bb --- /dev/null +++ b/src/slic3r/GUI/Widgets/StaticBox.cpp @@ -0,0 +1,216 @@ +#include "StaticBox.hpp" +#include "../GUI.hpp" +#include + +BEGIN_EVENT_TABLE(StaticBox, wxWindow) + +// catch paint events +//EVT_ERASE_BACKGROUND(StaticBox::eraseEvent) +EVT_PAINT(StaticBox::paintEvent) + +END_EVENT_TABLE() + +/* + * Called by the system of by wxWidgets when the panel needs + * to be redrawn. You can also trigger this call by + * calling Refresh()/Update(). + */ + +StaticBox::StaticBox() + : state_handler(this) + , radius(8) +{ + border_color = StateColor( + std::make_pair(0xF0F0F1, (int) StateColor::Disabled), + std::make_pair(0x303A3C, (int) StateColor::Normal)); +} + +StaticBox::StaticBox(wxWindow* parent, + wxWindowID id, + const wxPoint & pos, + const wxSize & size, long style) + : StaticBox() +{ + Create(parent, id, pos, size, style); +} + +bool StaticBox::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + if (style & wxBORDER_NONE) + border_width = 0; + wxWindow::Create(parent, id, pos, size, style); + state_handler.attach({&border_color, &background_color, &background_color2}); + state_handler.update_binds(); + SetBackgroundColour(GetParentBackgroundColor(parent)); + return true; +} + +void StaticBox::SetCornerRadius(double radius) +{ + this->radius = radius; + Refresh(); +} + +void StaticBox::SetBorderWidth(int width) +{ + border_width = width; + Refresh(); +} + +void StaticBox::SetBorderColor(StateColor const &color) +{ + border_color = color; + state_handler.update_binds(); + Refresh(); +} + +void StaticBox::SetBorderColorNormal(wxColor const &color) +{ + border_color.setColorForStates(color, 0); + Refresh(); +} + +void StaticBox::SetBackgroundColor(StateColor const &color) +{ + background_color = color; + state_handler.update_binds(); + Refresh(); +} + +void StaticBox::SetBackgroundColorNormal(wxColor const &color) +{ + background_color.setColorForStates(color, 0); + Refresh(); +} + +void StaticBox::SetBackgroundColor2(StateColor const &color) +{ + background_color2 = color; + state_handler.update_binds(); + Refresh(); +} + +wxColor StaticBox::GetParentBackgroundColor(wxWindow* parent) +{ + if (auto box = dynamic_cast(parent)) { + if (box->background_color.count() > 0) { + if (box->background_color2.count() == 0) + return box->background_color.defaultColor(); + auto s = box->background_color.defaultColor(); + auto e = box->background_color2.defaultColor(); + int r = (s.Red() + e.Red()) / 2; + int g = (s.Green() + e.Green()) / 2; + int b = (s.Blue() + e.Blue()) / 2; + return wxColor(r, g, b); + } + } + if (parent) + return parent->GetBackgroundColour(); + return *wxWHITE; +} + +void StaticBox::eraseEvent(wxEraseEvent& evt) +{ + // for transparent background, but not work +#ifdef __WXMSW__ + wxDC *dc = evt.GetDC(); + wxSize size = GetSize(); + wxClientDC dc2(GetParent()); + dc->Blit({0, 0}, size, &dc2, GetPosition()); +#endif +} + +void StaticBox::paintEvent(wxPaintEvent& evt) +{ + // depending on your system you may need to look at double-buffered dcs + wxPaintDC dc(this); + render(dc); +} + +/* + * Here we do the actual rendering. I put it in a separate + * method so that it can work no matter what type of DC + * (e.g. wxPaintDC or wxClientDC) is used. + */ +void StaticBox::render(wxDC& dc) +{ +#ifdef __WXMSW__ + if (radius == 0) { + doRender(dc); + return; + } + + wxSize size = GetSize(); + if (size.x <= 0 || size.y <= 0) + return; + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + //memdc.Blit({0, 0}, size, &dc, {0, 0}); + memdc.SetBackground(wxBrush(GetBackgroundColour())); + memdc.Clear(); + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif +} + +void StaticBox::doRender(wxDC& dc) +{ + wxSize size = GetSize(); + int states = state_handler.states(); + if (background_color2.count() == 0) { + if ((border_width && border_color.count() > 0) || background_color.count() > 0) { + wxRect rc(0, 0, size.x, size.y); + if (border_width && border_color.count() > 0) { + if (dc.GetContentScaleFactor() == 1.0) { + int d = floor(border_width / 2.0); + int d2 = floor(border_width - 1); + rc.x += d; + rc.width -= d2; + rc.y += d; + rc.height -= d2; + } else { + int d = 1; + rc.x += d; + rc.width -= d; + rc.y += d; + rc.height -= d; + } + dc.SetPen(wxPen(border_color.colorForStates(states), border_width)); + } else { + dc.SetPen(wxPen(background_color.colorForStates(states))); + } + if (background_color.count() > 0) + dc.SetBrush(wxBrush(background_color.colorForStates(states))); + else + dc.SetBrush(wxBrush(GetBackgroundColour())); + if (radius == 0) { + dc.DrawRectangle(rc); + } + else { + dc.DrawRoundedRectangle(rc, radius - border_width); + } + } + } + else { + wxColor start = background_color.colorForStates(states); + wxColor stop = background_color2.colorForStates(states); + int r = start.Red(), g = start.Green(), b = start.Blue(); + int dr = (int) stop.Red() - r, dg = (int) stop.Green() - g, db = (int) stop.Blue() - b; + int lr = 0, lg = 0, lb = 0; + for (int y = 0; y < size.y; ++y) { + dc.SetPen(wxPen(wxColor(r, g, b))); + dc.DrawLine(0, y, size.x, y); + lr += dr; while (lr >= size.y) { ++r, lr -= size.y; } while (lr <= -size.y) { --r, lr += size.y; } + lg += dg; while (lg >= size.y) { ++g, lg -= size.y; } while (lg <= -size.y) { --g, lg += size.y; } + lb += db; while (lb >= size.y) { ++b, lb -= size.y; } while (lb <= -size.y) { --b, lb += size.y; } + } + } +} diff --git a/src/slic3r/GUI/Widgets/StaticBox.hpp b/src/slic3r/GUI/Widgets/StaticBox.hpp new file mode 100644 index 0000000..871c565 --- /dev/null +++ b/src/slic3r/GUI/Widgets/StaticBox.hpp @@ -0,0 +1,62 @@ +#ifndef slic3r_GUI_StaticBox_hpp_ +#define slic3r_GUI_StaticBox_hpp_ + +#include "../wxExtensions.hpp" +#include "StateHandler.hpp" + +#include + +class StaticBox : public wxWindow +{ +public: + StaticBox(); + + StaticBox(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint & pos = wxDefaultPosition, + const wxSize & size = wxDefaultSize, + long style = 0); + + bool Create(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint & pos = wxDefaultPosition, + const wxSize & size = wxDefaultSize, + long style = 0); + + void SetCornerRadius(double radius); + + void SetBorderWidth(int width); + + void SetBorderColor(StateColor const & color); + + void SetBorderColorNormal(wxColor const &color); + + void SetBackgroundColor(StateColor const &color); + + void SetBackgroundColorNormal(wxColor const &color); + + void SetBackgroundColor2(StateColor const &color); + + static wxColor GetParentBackgroundColor(wxWindow * parent); + +protected: + void eraseEvent(wxEraseEvent& evt); + + void paintEvent(wxPaintEvent& evt); + + void render(wxDC& dc); + + virtual void doRender(wxDC& dc); + +protected: + double radius; + int border_width = 1; + StateHandler state_handler; + StateColor border_color; + StateColor background_color; + StateColor background_color2; + + DECLARE_EVENT_TABLE() +}; + +#endif // !slic3r_GUI_StaticBox_hpp_ diff --git a/src/slic3r/GUI/Widgets/WebView.cpp b/src/slic3r/GUI/Widgets/WebView.cpp index 3f19ab4..b617440 100644 --- a/src/slic3r/GUI/Widgets/WebView.cpp +++ b/src/slic3r/GUI/Widgets/WebView.cpp @@ -259,7 +259,7 @@ bool WebView::RunScript(wxWebView *webView, wxString const &javascript) #elif defined __WXMAC__ WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend(); int count = 0; - wxJSScriptWrapper wrapJS(javascript, &count); + wxJSScriptWrapper wrapJS(javascript, wxJSScriptWrapper::OutputType::JS_OUTPUT_RAW); Slic3r::GUI::WKWebView_evaluateJavaScript(wkWebView, wrapJS.GetWrappedCode(), nullptr); return true; #else diff --git a/src/slic3r/Utils/MacDarkMode.hpp b/src/slic3r/Utils/MacDarkMode.hpp index ea8a7ae..9a450c1 100644 --- a/src/slic3r/Utils/MacDarkMode.hpp +++ b/src/slic3r/Utils/MacDarkMode.hpp @@ -1,12 +1,21 @@ #ifndef slic3r_MacDarkMode_hpp_ #define slic3r_MacDarkMode_hpp_ +#include + namespace Slic3r { namespace GUI { #if __APPLE__ extern bool mac_dark_mode(); extern double mac_max_scaling_factor(); +extern void set_miniaturizable(void * window); +void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*callback)(wxString const &)); +void WKWebView_setTransparentBackground(void * web); +void set_tag_when_enter_full_screen(bool isfullscreen); +void set_title_colour_after_set_title(void * window); +void initGestures(void * view, wxEvtHandler * handler); +void openFolderForFile(wxString const & file); #endif diff --git a/src/slic3r/Utils/MacDarkMode.mm b/src/slic3r/Utils/MacDarkMode.mm index 512e96b..e539053 100644 --- a/src/slic3r/Utils/MacDarkMode.mm +++ b/src/slic3r/Utils/MacDarkMode.mm @@ -1,10 +1,16 @@ #import "MacDarkMode.hpp" +#include "../GUI/Widgets/Label.hpp" + +#include "wx/osx/core/cfstring.h" #import #import #import #import +#import + +#include @interface MacDarkMode : NSObject {} @end @@ -14,6 +20,9 @@ namespace Slic3r { namespace GUI { +NSTextField* mainframe_text_field = nil; +bool is_in_full_screen_mode = false; + bool mac_dark_mode() { NSString *style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; @@ -32,8 +41,327 @@ double mac_max_scaling_factor() } return scaling; } + +void set_miniaturizable(void * window) +{ + CGFloat rFloat = 34/255.0; + CGFloat gFloat = 34/255.0; + CGFloat bFloat = 36/255.0; + [(NSView*) window window].titlebarAppearsTransparent = true; + [(NSView*) window window].backgroundColor = [NSColor colorWithCalibratedRed:rFloat green:gFloat blue:bFloat alpha:1.0]; + [(NSView*) window window].styleMask |= NSMiniaturizableWindowMask; + NSEnumerator *viewEnum = [[[[[[[(NSView*) window window] contentView] superview] titlebarViewController] view] subviews] objectEnumerator]; + NSView *viewObject; + + while(viewObject = (NSView *)[viewEnum nextObject]) { + if([viewObject class] == [NSTextField self]) { + //[(NSTextField*)viewObject setTextColor : NSColor.whiteColor]; + mainframe_text_field = viewObject; + } + } +} + +void set_tag_when_enter_full_screen(bool isfullscreen) +{ + is_in_full_screen_mode = isfullscreen; +} + +void set_title_colour_after_set_title(void * window) +{ + NSEnumerator *viewEnum = [[[[[[[(NSView*) window window] contentView] superview] titlebarViewController] view] subviews] objectEnumerator]; + NSView *viewObject; + while(viewObject = (NSView *)[viewEnum nextObject]) { + if([viewObject class] == [NSTextField self]) { + [(NSTextField*)viewObject setTextColor : NSColor.whiteColor]; + mainframe_text_field = viewObject; + } + } + + if (mainframe_text_field) { + [(NSTextField*)mainframe_text_field setTextColor : NSColor.whiteColor]; + } +} + +void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*callback)(wxString const &)) +{ + [(WKWebView*)web evaluateJavaScript:wxCFStringRef(script).AsNSString() completionHandler: ^(id result, NSError *error) { + if (callback && error != nil) { + wxString err = wxCFStringRef(error.localizedFailureReason).AsString(); + callback(err); + } + }]; +} + +void WKWebView_setTransparentBackground(void * web) +{ + WKWebView * webView = (WKWebView*)web; + [webView layer].backgroundColor = [NSColor clearColor].CGColor; +} + +void openFolderForFile(wxString const & file) +{ + NSArray *fileURLs = [NSArray arrayWithObjects:wxCFStringRef(file).AsNSString(), /* ... */ nil]; + [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs]; +} + } } @end + +/* textColor for NSTextField */ +@implementation NSTextField (textColor) + +- (void)setTextColor2:(NSColor *)textColor +{ + if (Slic3r::GUI::mainframe_text_field != self){ + [self setTextColor2: textColor]; + }else{ + if(Slic3r::GUI::is_in_full_screen_mode){ + [self setTextColor2 : NSColor.darkGrayColor]; + }else{ + [self setTextColor2 : NSColor.whiteColor]; + } + } +} + + ++ (void) load +{ + Method setTextColor = class_getInstanceMethod([NSTextField class], @selector(setTextColor:)); + Method setTextColor2 = class_getInstanceMethod([NSTextField class], @selector(setTextColor2:)); + method_exchangeImplementations(setTextColor, setTextColor2); +} + +@end + +/* drawsBackground for NSTextField */ +@implementation NSTextField (drawsBackground) + +- (instancetype)initWithFrame2:(NSRect)frameRect +{ + [self initWithFrame2:frameRect]; + self.drawsBackground = false; + return self; +} + + ++ (void) load +{ + Method initWithFrame = class_getInstanceMethod([NSTextField class], @selector(initWithFrame:)); + Method initWithFrame2 = class_getInstanceMethod([NSTextField class], @selector(initWithFrame2:)); + method_exchangeImplementations(initWithFrame, initWithFrame2); +} + +@end + +/* textColor for NSButton */ + +@implementation NSButton (NSButton_Extended) + +- (NSColor *)textColor +{ + NSAttributedString *attrTitle = [self attributedTitle]; + int len = [attrTitle length]; + NSRange range = NSMakeRange(0, MIN(len, 1)); // get the font attributes from the first character + NSDictionary *attrs = [attrTitle fontAttributesInRange:range]; + NSColor *textColor = [NSColor controlTextColor]; + if (attrs) + { + textColor = [attrs objectForKey:NSForegroundColorAttributeName]; + } + + return textColor; +} + +- (void)setTextColor:(NSColor *)textColor +{ + NSMutableAttributedString *attrTitle = + [[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTitle]]; + int len = [attrTitle length]; + NSRange range = NSMakeRange(0, len); + [attrTitle addAttribute:NSForegroundColorAttributeName value:textColor range:range]; + [attrTitle fixAttributesInRange:range]; + [self setAttributedTitle:attrTitle]; + [attrTitle release]; +} + +- (void)setBezelStyle2:(NSBezelStyle)bezelStyle +{ + if (bezelStyle != NSBezelStyleShadowlessSquare) + [self setBordered: YES]; + [self setBezelStyle2: bezelStyle]; +} + ++ (void) load +{ + Method setBezelStyle = class_getInstanceMethod([NSButton class], @selector(setBezelStyle:)); + Method setBezelStyle2 = class_getInstanceMethod([NSButton class], @selector(setBezelStyle2:)); + method_exchangeImplementations(setBezelStyle, setBezelStyle2); +} + +- (NSFocusRingType) focusRingType +{ + return NSFocusRingTypeNone; +} + +@end + +/* edit column for wxCocoaOutlineView */ + +#include +#include +#include + +@implementation wxCocoaOutlineView (Edit) + +bool addObserver = false; + +- (BOOL)outlineView: (NSOutlineView*) view shouldEditTableColumn:(nullable NSTableColumn *)tableColumn item:(nonnull id)item +{ + NSClipView * clipView = [[self enclosingScrollView] contentView]; + if (!addObserver) { + addObserver = true; + clipView.postsBoundsChangedNotifications = YES; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(synchronizedViewContentBoundsDidChange:) + name:NSViewBoundsDidChangeNotification + object:clipView]; + } + + wxDataViewColumn* const col((wxDataViewColumn *)[tableColumn getColumnPointer]); + wxDataViewItem item2([static_cast(item) pointer]); + + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); + // Before doing anything we send an event asking if editing of this item is really wanted. + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, dvc, col, item2); + dvc->GetEventHandler()->ProcessEvent( event ); + if( !event.IsAllowed() ) + return NO; + + return YES; +} + +- (void)synchronizedViewContentBoundsDidChange:(NSNotification *)notification +{ + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); + wxDataViewCustomRenderer * r = dvc->GetCustomRendererPtr(); + if (r) + r->FinishEditing(); +} + +@end + +/* Font for wxTextCtrl */ + +@implementation NSTableHeaderCell (Font) + +- (NSFont*) font +{ + return Label::sysFont(13).OSXGetNSFont(); +} + +@end + +/* remove focused border for wxTextCtrl */ + +@implementation NSTextField (FocusRing) + +- (NSFocusRingType) focusRingType +{ + return NSFocusRingTypeNone; +} + +@end + +/* gesture handle for Canvas3D */ + +@interface wxNSCustomOpenGLView : NSOpenGLView +{ +} +@end + + +@implementation wxNSCustomOpenGLView (Gesture) + +wxEvtHandler * _gestureHandler = nullptr; + +- (void) onGestureMove: (NSPanGestureRecognizer*) gesture +{ + wxPanGestureEvent evt; + NSPoint tr = [gesture translationInView: self]; + evt.SetDelta({(int) tr.x, (int) tr.y}); + [self postEvent:evt withGesture:gesture]; +} + +- (void) onGestureScale: (NSMagnificationGestureRecognizer*) gesture +{ + wxZoomGestureEvent evt; + evt.SetZoomFactor(gesture.magnification + 1.0); + [self postEvent:evt withGesture:gesture]; +} + +- (void) onGestureRotate: (NSRotationGestureRecognizer*) gesture +{ + wxRotateGestureEvent evt; + evt.SetRotationAngle(-gesture.rotation); + [self postEvent:evt withGesture:gesture]; +} + +- (void) postEvent: (wxGestureEvent &) evt withGesture: (NSGestureRecognizer* ) gesture +{ + NSPoint pos = [gesture locationInView: self]; + evt.SetPosition({(int) pos.x, (int) pos.y}); + if (gesture.state == NSGestureRecognizerStateBegan) + evt.SetGestureStart(); + else if (gesture.state == NSGestureRecognizerStateEnded) + evt.SetGestureEnd(); + _gestureHandler->ProcessEvent(evt); +} + +- (void) scrollWheel2:(NSEvent *)event +{ + bool shiftDown = [event modifierFlags] & NSShiftKeyMask; + if (_gestureHandler && shiftDown && event.hasPreciseScrollingDeltas) { + wxPanGestureEvent evt; + evt.SetDelta({-(int)[event scrollingDeltaX], - (int)[event scrollingDeltaY]}); + _gestureHandler->ProcessEvent(evt); + } else { + [self scrollWheel2: event]; + } +} + ++ (void) load +{ + Method scrollWheel = class_getInstanceMethod([wxNSCustomOpenGLView class], @selector(scrollWheel:)); + Method scrollWheel2 = class_getInstanceMethod([wxNSCustomOpenGLView class], @selector(scrollWheel2:)); + method_exchangeImplementations(scrollWheel, scrollWheel2); +} + +- (void) initGesturesWithHandler: (wxEvtHandler*) handler +{ +// NSPanGestureRecognizer * pan = [[NSPanGestureRecognizer alloc] initWithTarget: self action: @selector(onGestureMove:)]; +// pan.numberOfTouchesRequired = 2; +// pan.allowedTouchTypes = 0; +// NSMagnificationGestureRecognizer * magnification = [[NSMagnificationGestureRecognizer alloc] initWithTarget: self action: @selector(onGestureScale:)]; +// NSRotationGestureRecognizer * rotation = [[NSRotationGestureRecognizer alloc] initWithTarget: self action: @selector(onGestureRotate:)]; +// [self addGestureRecognizer:pan]; +// [self addGestureRecognizer:magnification]; +// [self addGestureRecognizer:rotation]; + _gestureHandler = handler; +} + +@end + +namespace Slic3r { +namespace GUI { + +void initGestures(void * view, wxEvtHandler * handler) +{ + wxNSCustomOpenGLView * glView = (wxNSCustomOpenGLView *) view; + [glView initGesturesWithHandler: handler]; +} + +} +}