【问题标题】:(Push + Dropdown) button wxWidgets(Push + Dropdown) 按钮 wxWidgets
【发布时间】:2016-05-06 16:06:14
【问题描述】:

我需要一个具有按钮和下拉按钮的控件。

例如

wxRibbonButtonBar 中提供了类似的控件,我无法在简单的 wxPanel 中使用它。

【问题讨论】:

    标签: wxwidgets


    【解决方案1】:

    我实现了 SplitButton,它看起来像:

    SplitButton.h

    #ifndef __SPLIT_BUTTON_H__
    #define __SPLIT_BUTTON_H__
    
    #include <wx/wx.h>
    #include <wx/menu.h>
    
    class SplitButton : public wxPanel
    {
    public:
        SplitButton(wxWindow *parent,
            wxWindowID id,
            const wxString& label,
            const wxPoint& pos = wxDefaultPosition,
            const wxSize& size = wxDefaultSize);
    
        ~SplitButton();
    
        wxMenu* GetSplitButtonMenu();
    
    protected:
        void OnKillFocus(wxFocusEvent& event);
        void OnMouseLeave(wxMouseEvent& event);
        void OnMouseEnter(wxMouseEvent& event);
        void OnLeftButtonUp(wxMouseEvent& event);
        void OnLeftButtonDown(wxMouseEvent& event);
        void OnPaint(wxPaintEvent& WXUNUSED(event));
    
        bool Enable(bool enable = true) override;
    
    private:
        int m_stateButton = 0;
        int m_stateMenu = 0;
        bool m_bIsEnable = true;
        wxColor m_colorNormal;
        wxColor m_colorDisabled;
        const int m_arrowButtonWidth = 20;
        bool m_bLButtonDown = false;
        wxString m_label;
        wxMenu* m_pMenu = nullptr;
    };
    
    #endif /*__SPLIT_BUTTON_H__*/
    

    SplitButton.cpp

    #include "SplitButton.h"
    #include <wx/renderer.h>
    
    SplitButton::SplitButton(wxWindow *parent,
        wxWindowID id,
        const wxString& label,
        const wxPoint& pos,
        const wxSize& size)
        : wxPanel(parent, id, pos, size, wxBORDER_NONE | wxTAB_TRAVERSAL, "DropDownButton"),
        m_label(label)
    {
        m_colorNormal = GetForegroundColour();
        m_colorDisabled = GetForegroundColour().MakeDisabled();
    
        if (size == wxDefaultSize)
        {
            wxSize defaultSize = wxButton::GetDefaultSize();
    
            wxSize textSize = GetTextExtent(m_label);
            textSize.SetWidth(textSize.GetWidth() + m_arrowButtonWidth + 20);
            SetMinSize(wxSize(textSize.GetWidth(), defaultSize.GetHeight()));
        }
    
        Bind(wxEVT_PAINT, &SplitButton::OnPaint, this);
        Bind(wxEVT_LEFT_UP, &SplitButton::OnLeftButtonUp, this);
        Bind(wxEVT_LEFT_DOWN, &SplitButton::OnLeftButtonDown, this);
        Bind(wxEVT_KILL_FOCUS, &SplitButton::OnKillFocus, this);
        Bind(wxEVT_LEAVE_WINDOW, &SplitButton::OnMouseLeave, this);
        Bind(wxEVT_ENTER_WINDOW, &SplitButton::OnMouseEnter, this);
    
        m_pMenu = new wxMenu();
    }
    
    SplitButton::~SplitButton()
    {
        delete m_pMenu;
        m_pMenu = nullptr;
    }
    
    wxMenu* SplitButton::GetSplitButtonMenu()
    {
        return m_pMenu;
    }
    
    void SplitButton::OnKillFocus(wxFocusEvent& event)
    {
        m_stateButton = wxCONTROL_CURRENT;
        m_stateMenu = wxCONTROL_CURRENT;
        Refresh();
    
        event.Skip();
    }
    
    void SplitButton::OnMouseLeave(wxMouseEvent& event)
    {
        m_stateButton = 0;
        m_stateMenu = 0;
        Refresh();
    
        event.Skip();
    }
    
    void SplitButton::OnMouseEnter(wxMouseEvent& event)
    {
        m_stateButton = wxCONTROL_CURRENT;
        m_stateMenu = wxCONTROL_CURRENT;
        Refresh();
    
        event.Skip();
    }
    
    void SplitButton::OnLeftButtonUp(wxMouseEvent& event)
    {
        m_stateButton = 0;
        m_stateMenu = 0;
    
        Refresh();
    
        int x = -1;
        int y = -1;
        event.GetPosition(&x, &y);
    
        if (x < (GetSize().GetWidth() - m_arrowButtonWidth))
        {
            wxEvtHandler* pEventHandler = GetEventHandler();
            wxASSERT(pEventHandler);
    
            pEventHandler->CallAfter([=]()
            {
                wxCommandEvent evt(wxEVT_BUTTON, this->GetId());
                evt.SetEventObject(this);
                GetEventHandler()->ProcessEvent(evt);
            });
        }
    
        m_bLButtonDown = false;
    
        event.Skip();
    }
    
    void SplitButton::OnLeftButtonDown(wxMouseEvent& event)
    {
        m_bLButtonDown = true;
    
        int x = -1;
        int y = -1;
        event.GetPosition(&x, &y);
    
        if (x >= (GetSize().GetWidth() - m_arrowButtonWidth))
        {
            m_stateButton = 0;
            m_stateMenu = wxCONTROL_PRESSED;
            Refresh();
    
            wxSize size = GetSize();
            wxPoint position;
            position.x = 0;
            position.y = size.GetHeight();
            PopupMenu(m_pMenu, position);
    
            m_stateMenu = 0;
            Refresh();
        }
        else
        {
            m_stateButton = wxCONTROL_PRESSED;
            m_stateMenu = wxCONTROL_PRESSED;
            Refresh();
        }
    
        event.Skip();
    }
    
    void SplitButton::OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxPaintDC dc(this);
        wxSize size = GetSize();
        const int width = size.GetWidth() - m_arrowButtonWidth;
    
        // Draw first part of button
        wxRect r1;
        r1.x = 0;
        r1.y = 0;
        r1.width = width + 2;
        r1.height = size.GetHeight();
    
        wxRendererNative::Get().DrawPushButton(this, dc, r1, m_stateButton);
    
        SetForegroundColour(m_bIsEnable ? m_colorNormal : m_colorDisabled);
    
        r1.y += (size.GetHeight() - GetCharHeight()) / 2;
        dc.DrawLabel(m_label, r1, wxALIGN_CENTER_HORIZONTAL);
    
        // Draw second part of button
        wxRect r2;
        r2.x = width - 2;
        r2.y = 0;
        r2.width = m_arrowButtonWidth;
        r2.height = size.GetHeight();
    
        wxRendererNative::Get().DrawPushButton(this, dc, r2, m_stateMenu);
        wxRendererNative::Get().DrawDropArrow(this, dc, r2, m_stateMenu);
    }
    
    bool SplitButton::Enable(bool enable)
    {
        m_bIsEnable = enable;
        wxPanel::Enable(m_bIsEnable);
    
        if (m_bIsEnable)
        {
            m_stateButton = 0;
            m_stateMenu = 0;
        }
        else
        {
            m_stateButton = wxCONTROL_DISABLED;
            m_stateMenu = wxCONTROL_DISABLED;
        }
    
        wxPaintEvent event;
        ProcessEvent(event);
        Refresh();
    
        return enable;
    }
    

    【讨论】:

    • 这看起来很完美。我去看看。顺便说一句,你在我生日那天写了这个答案!
    【解决方案2】:

    这种按钮没有 wxwidgets。但是如果你使用的是win32并且有wxwidgets 3.1+,你可以试试nativewindow

    查看 wxwidget 包附带的示例文件

    %wxpath%\wxWidgets-3.1.0\samples\widgets\native.cpp

    class NativeWindow : public wxNativeWindow
    {
    public:
        explicit NativeWindow(wxWindow* parent)
            : wxNativeWindow()
        {
            // When creating the native window, we must specify the valid parent
            // and while we don't have to specify any position if it's going to be
            // laid out by sizers, we do need the size.
            const wxSize size = FromDIP(wxSize(140, 30));
    
            HWND hwnd = ::CreateWindow
                          (
                            TEXT("BUTTON"),
                            TEXT("Press me to do it"),
                            WS_CHILD | WS_VISIBLE | BS_SPLITBUTTON,
                            0, 0, size.x, size.y,
                            (HWND)parent->GetHWND(), 0, NULL, NULL
                          );
            if ( !hwnd )
            {
                wxLogError("Creating split button failed.");
                return;
            }
    
            (void)Create(parent, wxID_ANY, hwnd);
        }
    

    更新:一个解决方法是使用两个按钮来模拟组合按钮,不要忘记设置 wxBU_EXACTFIT 并调整板

    【讨论】:

    • 我使用的是 wx 3.0,我在 Mac 上也需要这个控件。
    • @Anil8753 您也可以使用两个按钮作为解决方法,请参阅我的更新答案
    【解决方案3】:

    我建议在 3.1+ 中也使用 wxNativeWindow,但是还有另一种可能对您有用的解决方案,并且在 3.0 中可用:使用 wxToolBar 并使用 wxITEM_DROPDOWN 样式添加工具。

    【讨论】:

    • 带有 wxITEM_DROPDOWN 工具的 wxToolbar 很有帮助,但并不完美。我有透明背景,鼠标悬停时显示完整按钮。我需要它的鼠标悬停状态始终可见
    • @Anil8753 你可以尝试修改WX的源代码。
    【解决方案4】:

    看看 wxOwnderDrawComboBox。

    【讨论】:

      猜你喜欢
      • 2018-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-28
      • 1970-01-01
      • 1970-01-01
      • 2019-04-21
      相关资源
      最近更新 更多