#ifndef slic3r_Notebook_hpp_ #define slic3r_Notebook_hpp_ #ifdef _WIN32 #include class ModeSizer; class ScalableButton; // custom message the ButtonsListCtrl sends to its parent (Notebook) to notify a selection change: wxDECLARE_EVENT(wxCUSTOMEVT_NOTEBOOK_SEL_CHANGED, wxCommandEvent); class ButtonsListCtrl : public wxControl { public: ButtonsListCtrl(wxWindow* parent, bool add_mode_buttons = false); ~ButtonsListCtrl() {} void OnPaint(wxPaintEvent&); void SetSelection(int sel); void UpdateMode(); void Rescale(); bool InsertPage(size_t n, const wxString& text, bool bSelect = false, const std::string& bmp_name = ""); void RemovePage(size_t n); bool SetPageImage(size_t n, const std::string& bmp_name) const; void SetPageText(size_t n, const wxString& strText); wxString GetPageText(size_t n) const; private: wxWindow* m_parent; wxFlexGridSizer* m_buttons_sizer; wxBoxSizer* m_sizer; std::vector m_pageButtons; int m_selection {-1}; int m_btn_margin; int m_line_margin; ModeSizer* m_mode_sizer {nullptr}; }; class Notebook: public wxBookCtrlBase { public: Notebook(wxWindow * parent, wxWindowID winid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0, bool add_mode_buttons = false) { Init(); Create(parent, winid, pos, size, style, add_mode_buttons); } bool Create(wxWindow * parent, wxWindowID winid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0, bool add_mode_buttons = false) { if (!wxBookCtrlBase::Create(parent, winid, pos, size, style | wxBK_TOP)) return false; m_bookctrl = new ButtonsListCtrl(this, add_mode_buttons); wxSizer* mainSizer = new wxBoxSizer(IsVertical() ? wxVERTICAL : wxHORIZONTAL); if (style & wxBK_RIGHT || style & wxBK_BOTTOM) mainSizer->Add(0, 0, 1, wxEXPAND, 0); m_controlSizer = new wxBoxSizer(IsVertical() ? wxHORIZONTAL : wxVERTICAL); m_controlSizer->Add(m_bookctrl, wxSizerFlags(1).Expand()); wxSizerFlags flags; if (IsVertical()) flags.Expand(); else flags.CentreVertical(); mainSizer->Add(m_controlSizer, flags.Border(wxALL, m_controlMargin)); SetSizer(mainSizer); this->Bind(wxCUSTOMEVT_NOTEBOOK_SEL_CHANGED, [this](wxCommandEvent& evt) { if (int page_idx = evt.GetId(); page_idx >= 0) SetSelection(page_idx); }); this->Bind(wxEVT_NAVIGATION_KEY, &Notebook::OnNavigationKey, this); return true; } // Methods specific to this class. // A method allowing to add a new page without any label (which is unused // by this control) and show it immediately. bool ShowNewPage(wxWindow * page) { return AddPage(page, wxString(), ""/*true *//* select it */); } // Set effect to use for showing/hiding pages. void SetEffects(wxShowEffect showEffect, wxShowEffect hideEffect) { m_showEffect = showEffect; m_hideEffect = hideEffect; } // Or the same effect for both of them. void SetEffect(wxShowEffect effect) { SetEffects(effect, effect); } // And the same for time outs. void SetEffectsTimeouts(unsigned showTimeout, unsigned hideTimeout) { m_showTimeout = showTimeout; m_hideTimeout = hideTimeout; } void SetEffectTimeout(unsigned timeout) { SetEffectsTimeouts(timeout, timeout); } // Implement base class pure virtual methods. // adds a new page to the control bool AddPage(wxWindow* page, const wxString& text, const std::string& bmp_name, bool bSelect = false) { DoInvalidateBestSize(); return InsertPage(GetPageCount(), page, text, bmp_name, bSelect); } // Page management virtual bool InsertPage(size_t n, wxWindow * page, const wxString & text, bool bSelect = false, int imageId = NO_IMAGE) override { if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect, imageId)) return false; GetBtnsListCtrl()->InsertPage(n, text, bSelect); if (!DoSetSelectionAfterInsertion(n, bSelect)) page->Hide(); return true; } bool InsertPage(size_t n, wxWindow * page, const wxString & text, const std::string& bmp_name = "", bool bSelect = false) { if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect)) return false; GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name); if (bSelect) SetSelection(n); return true; } virtual int SetSelection(size_t n) override { GetBtnsListCtrl()->SetSelection(n); int ret = DoSetSelection(n, SetSelection_SendEvent); // check that only the selected page is visible and others are hidden: for (size_t page = 0; page < m_pages.size(); page++) if (page != n) m_pages[page]->Hide(); return ret; } virtual int ChangeSelection(size_t n) override { GetBtnsListCtrl()->SetSelection(n); return DoSetSelection(n); } // Neither labels nor images are supported but we still store the labels // just in case the user code attaches some importance to them. virtual bool SetPageText(size_t n, const wxString & strText) override { wxCHECK_MSG(n < GetPageCount(), false, wxS("Invalid page")); GetBtnsListCtrl()->SetPageText(n, strText); return true; } virtual wxString GetPageText(size_t n) const override { wxCHECK_MSG(n < GetPageCount(), wxString(), wxS("Invalid page")); return GetBtnsListCtrl()->GetPageText(n); } virtual bool SetPageImage(size_t WXUNUSED(n), int WXUNUSED(imageId)) override { return false; } virtual int GetPageImage(size_t WXUNUSED(n)) const override { return NO_IMAGE; } bool SetPageImage(size_t n, const std::string& bmp_name) { return GetBtnsListCtrl()->SetPageImage(n, bmp_name); } // Override some wxWindow methods too. virtual void SetFocus() override { wxWindow* const page = GetCurrentPage(); if (page) page->SetFocus(); } ButtonsListCtrl* GetBtnsListCtrl() const { return static_cast(m_bookctrl); } void UpdateMode() { GetBtnsListCtrl()->UpdateMode(); } void Rescale() { GetBtnsListCtrl()->Rescale(); } void Notebook::OnNavigationKey(wxNavigationKeyEvent& event) { if (event.IsWindowChange()) { // change pages AdvanceSelection(event.GetDirection()); } else { // we get this event in 3 cases // // a) one of our pages might have generated it because the user TABbed // out from it in which case we should propagate the event upwards and // our parent will take care of setting the focus to prev/next sibling // // or // // b) the parent panel wants to give the focus to us so that we // forward it to our selected page. We can't deal with this in // OnSetFocus() because we don't know which direction the focus came // from in this case and so can't choose between setting the focus to // first or last panel child // // or // // c) we ourselves (see MSWTranslateMessage) generated the event // wxWindow* const parent = GetParent(); // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE const bool isFromParent = event.GetEventObject() == (wxObject*)parent; const bool isFromSelf = event.GetEventObject() == (wxObject*)this; const bool isForward = event.GetDirection(); if (isFromSelf && !isForward) { // focus is currently on notebook tab and should leave // it backwards (Shift-TAB) event.SetCurrentFocus(this); parent->HandleWindowEvent(event); } else if (isFromParent || isFromSelf) { // no, it doesn't come from child, case (b) or (c): forward to a // page but only if entering notebook page (i.e. direction is // backwards (Shift-TAB) comething from out-of-notebook, or // direction is forward (TAB) from ourselves), if (m_selection != wxNOT_FOUND && (!event.GetDirection() || isFromSelf)) { // so that the page knows that the event comes from it's parent // and is being propagated downwards event.SetEventObject(this); wxWindow* page = m_pages[m_selection]; if (!page->HandleWindowEvent(event)) { page->SetFocus(); } //else: page manages focus inside it itself } else // otherwise set the focus to the notebook itself { SetFocus(); } } else { // it comes from our child, case (a), pass to the parent, but only // if the direction is forwards. Otherwise set the focus to the // notebook itself. The notebook is always the 'first' control of a // page. if (!isForward) { SetFocus(); } else if (parent) { event.SetCurrentFocus(this); parent->HandleWindowEvent(event); } } } } protected: virtual void UpdateSelectedPage(size_t WXUNUSED(newsel)) override { // Nothing to do here, but must be overridden to avoid the assert in // the base class version. } virtual wxBookCtrlEvent * CreatePageChangingEvent() const override { return new wxBookCtrlEvent(wxEVT_BOOKCTRL_PAGE_CHANGING, GetId()); } virtual void MakeChangedEvent(wxBookCtrlEvent & event) override { event.SetEventType(wxEVT_BOOKCTRL_PAGE_CHANGED); } virtual wxWindow * DoRemovePage(size_t page) override { wxWindow* const win = wxBookCtrlBase::DoRemovePage(page); if (win) { GetBtnsListCtrl()->RemovePage(page); DoSetSelectionAfterRemoval(page); } return win; } virtual void DoSize() override { wxWindow* const page = GetCurrentPage(); if (page) page->SetSize(GetPageRect()); } virtual void DoShowPage(wxWindow * page, bool show) override { if (show) page->ShowWithEffect(m_showEffect, m_showTimeout); else page->HideWithEffect(m_hideEffect, m_hideTimeout); } private: void Init() { // We don't need any border as we don't have anything to separate the // page contents from. SetInternalBorder(0); // No effects by default. m_showEffect = m_hideEffect = wxSHOW_EFFECT_NONE; m_showTimeout = m_hideTimeout = 0; } wxShowEffect m_showEffect, m_hideEffect; unsigned m_showTimeout, m_hideTimeout; ButtonsListCtrl* m_ctrl{ nullptr }; }; #endif // _WIN32 #endif // slic3r_Notebook_hpp_