简体   繁体   English

在 wxWidgets 中创建无标题/无边框可拖动 wxFrame

[英]Create a Titleless/Borderless draggable wxFrame in wxWidgets

Hi i am trying to create a wxApp that will have not have a titlebar(including the minimize, maximize and close) icons that are by default provided.嗨,我正在尝试创建一个 wxApp,它没有默认提供的标题栏(包括最小化、最大化和关闭)图标。 The code that i have is as follows: main.h我的代码如下: main.h

class MyApp : public wxApp
{
  public:
    virtual bool OnInit();
};

main.cpp主程序

bool MyApp::OnInit()
{

    MyFrame *prop = new MyFrame(wxT("MyFrame"));
    prop->Show(true);

    return true;
}

myframe.h myframe.h

class MyFrame : public wxFrame
{
public:
  MyFrame(const wxString& title);
  
  void OnClick(wxMouseEvent& event);

};

myframe.cpp myframe.cpp

MyFrame::MyFrame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
  MyPanel *panel = new MyPanel(this, wxID_ANY);
  panel->SetBackgroundColour(wxColour(255,255,255));
  MyButton *button = new MyButton(panel, wxID_ANY, wxT("Ok"));

  Connect( wxEVT_LEFT_UP, 
      wxMouseEventHandler(MyFrame::OnClick));

  panel->Centre();
}


void MyFrame::OnClick(wxMouseEvent& event) 
{
  std::cout << "event reached frame class" << std::endl;
  
}

mypanel.h我的面板.h

class MyPanel : public wxPanel
{
public: 
  MyPanel(wxFrame *frame, int id);

  void OnClick(wxMouseEvent& event);
};

mypanel.cpp我的面板.cpp

MyPanel::MyPanel(wxFrame *frame, int id)
    : wxPanel(frame, id)
{
  Connect( wxEVT_LEFT_UP, 
      wxMouseEventHandler(MyPanel::OnClick));
} 

void MyPanel::OnClick(wxMouseEvent& event) 
{
  std::cout << "event reached panel class" << std::endl;
  
}

mybutton.h我的按钮.h

class MyButton : public wxPanel
{
public:
  MyButton(wxPanel *panel, int id, const wxString &label);

  void OnClick(wxMouseEvent& event);

};

mybutton.cpp mybutton.cpp

void MyButton::onClick(wxMouseEvent &event)
{

}

What i want is:我想要的是:

  1. There should be no title bar(including the 3 maximize, minimize & close buttons) at the top.顶部不应有标题栏(包括 3 个最大化、最小化和关闭按钮)。
  2. Now since there is no titlebar at the top of the frame, there is no way to drag or close or maximize or minimize the window.现在由于框架顶部没有标题栏,因此无法拖动或关闭或最大化或最小化窗口。 For that i want to create a custom titlebar at the top which should have the three customized maximized,minimized and close button and also i should be able to drag the frame by double clicking and holding and dragging the topmost part of the newly created frame.为此,我想在顶部创建一个自定义标题栏,它应该具有三个自定义的最大化、最小化和关闭按钮,而且我应该能够通过双击并按住并拖动新创建的框架的最顶部来拖动框架。

Is this possible in wxWidgets?这在 wxWidgets 中可能吗? If yes, how can i achieve this?如果是,我怎样才能做到这一点?

I am not proposing any new way of dragging.我没有提出任何新的拖动方式。 The new frame/window that we will have should also be dragged only by its own customized title bar.我们将拥有的新框架/窗口也应该仅由其自定义标题栏拖动。 It's exactly like dragging the old frame by double clicking and dragging the frame in the native case.就像在原生情况下通过双击并拖动框架来拖动旧框架一样。 I just want to customize the native title bar.我只想自定义本机标题栏。 Like increase its height, change it's colour, change how to three buttons(minimize, maximize and close) look.比如增加它的高度,改变它的颜色,改变三个按钮(最小化,最大化和关闭)的外观。

Here's the simplest example I can think of how to create a frame with a pseudo titlebar that can be clicked to drag the frame around.这是我能想到的最简单的例子,如何创建一个带有伪标题栏的框架,可以点击它来拖动框架。 This example shows which mouse events need to be handled to drag the window around and how to do the calculations needed in those event handlers.此示例显示需要处理哪些鼠标事件以拖动窗口以及如何在这些事件处理程序中进行所需的计算。

Note that moving the frame needs to be done in screen coordinates, but the coordinates received in the event handlers will be in client coordinates for the title bar.请注意,移动框架需要在屏幕坐标中完成,但在事件处理程序中接收到的坐标将在标题栏的客户端坐标中。 This example also shows how to do those coordinate conversions.此示例还展示了如何进行这些坐标转换。

#include "wx/wx.h"

class CustomTitleBar:public wxWindow
{
public:
    CustomTitleBar(wxWindow* p) : wxWindow(p,wxID_ANY)
    {
        m_dragging = false;

        SetBackgroundColour(*wxGREEN);
        Bind(wxEVT_LEFT_DOWN,&CustomTitleBar::OnMouseLeftDown,this);
        Bind(wxEVT_MOUSE_CAPTURE_LOST, &CustomTitleBar::OnMouseCaptureLost,
             this);
    }

    wxSize DoGetBestClientSize() const override
    {
        return wxSize(-1,20);
    }

private:
    void OnMouseLeftDown(wxMouseEvent& event)
    {
        if ( !m_dragging )
        {
            Bind(wxEVT_LEFT_UP,&CustomTitleBar::OnMouseLeftUp,this);
            Bind(wxEVT_MOTION,&CustomTitleBar::OnMouseMotion,this);
            m_dragging = true;

            wxPoint clientStart = event.GetPosition();
            m_dragStartMouse = ClientToScreen(clientStart);
            m_dragStartWindow = GetParent()->GetPosition();
            CaptureMouse();
        }
    }

    void OnMouseLeftUp(wxMouseEvent&)
    {
        FinishDrag();
    }

    void OnMouseMotion(wxMouseEvent& event)
    {
        wxPoint curClientPsn = event.GetPosition();
        wxPoint curScreenPsn = ClientToScreen(curClientPsn);
        wxPoint movementVector = curScreenPsn - m_dragStartMouse;

        GetParent()->SetPosition(m_dragStartWindow + movementVector);
    }

    void OnMouseCaptureLost(wxMouseCaptureLostEvent&)
    {
        FinishDrag();
    }

    void FinishDrag()
    {
        if ( m_dragging )
        {
            Unbind(wxEVT_LEFT_UP,&CustomTitleBar::OnMouseLeftUp,this);
            Unbind(wxEVT_MOTION,&CustomTitleBar::OnMouseMotion,this);
            m_dragging = false;
        }

        if ( HasCapture() )
        {
            ReleaseMouse();
        }
    }

    wxPoint m_dragStartMouse;
    wxPoint m_dragStartWindow;
    bool m_dragging;
};

class Customframe : public wxFrame
{
public:
    Customframe(wxWindow* p)
        :wxFrame(p, wxID_ANY, wxString(), wxDefaultPosition, wxSize(150,100),
                 wxBORDER_NONE)
    {
        CustomTitleBar* t = new CustomTitleBar(this);
        SetBackgroundColour(*wxBLUE);
        wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
        szr->Add(t,wxSizerFlags(0).Expand());
        SetSizer(szr);
        Layout();
    }
};

class MyFrame: public wxFrame
{
public:
    MyFrame():wxFrame(NULL, wxID_ANY, "Custom frame Demo", wxDefaultPosition,
             wxSize(400, 300))
     {
         wxPanel* bg = new wxPanel(this, wxID_ANY);
         wxButton* btn = new wxButton(bg, wxID_ANY, "Custom frame");
         wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
         szr->Add(btn,wxSizerFlags(0).Border(wxALL));
         bg->SetSizer(szr);

         Layout();

         btn->Bind(wxEVT_BUTTON, &MyFrame::OnButton, this);
         m_customFrame = NULL;
     }
private:
    void OnButton(wxCommandEvent&)
    {
        if ( m_customFrame )
        {
            m_customFrame->Close();
            m_customFrame = NULL;
        }
        else
        {
            m_customFrame = new Customframe(this);
            m_customFrame->CenterOnParent();
            m_customFrame->Show();
        }
    }

    wxFrame* m_customFrame;
};

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            MyFrame* frame = new MyFrame();
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);

On windows, it looks like this.在 Windows 上,它看起来像这样。

带有自定义标题栏的自定义框架

You should be able to add whatever buttons you want to the custom title bar exactly as you would add buttons to any other window.您应该能够将任何您想要的按钮添加到自定义标题栏,就像您将按钮添加到任何其他窗口一样。

" Is this possible in wxWidgets? " 这在 wxWidgets 中可行吗?
Yes.是的。 You need to use a wxWindow instead of a wxFrame and set some style for it.您需要使用wxWindow而不是 wxFrame 并为其设置一些样式。 like wxBORDER_NONE .wxBORDER_NONE But you will have to implement many things that wxFrame already provides.但是您必须实现 wxFrame 已经提供的许多东西。

And your proposed way of dragging seems wrong/confusing to me.你提议的拖动方式对我来说似乎是错误的/令人困惑的。 99.99% of users prefer a UI they are used to it, avoiding to learn a new way of doing simple things they already know. 99.99% 的用户更喜欢他们习惯的 UI,避免学习一种新的方式来做他们已经知道的简单事情。

If you just want to avoid resizing then you have two ways:如果您只想避免调整大小,那么您有两种方法:

  • a) Catch the size-event and do nothing. a) 捕捉大小事件并且什么都不做。 Calling event.Skip(false) (to prevent handling in parent) is not neccessary. event.Skip(false)调用event.Skip(false) (以防止在父event.Skip(false)处理)。
  • b) Once you have created your window and correctly sized, get its size and set it as max and min. b) 创建窗口并正确调整大小后,获取其大小并将其设置为最大和最小。

In both cases the user will see a "resize" mouse pointer when hovering the mouse at any border, but nothing else, no resizing, will be done.在这两种情况下,用户将鼠标悬停在任何边框上时都会看到“调整大小”鼠标指针,但不会执行任何其他操作,不调整大小。

@JasonLiam, @杰森利亚姆,

Don't forget to place the application icon in the top left corner of you title bar and handle the right/left mouse clicks appropriately (as in native application) (if you want to go this route and get rid of the native frame window).不要忘记将应用程序图标放置在标题栏的左上角并适当处理鼠标右键/左键单击(如在本机应用程序中)(如果您想走这条路并摆脱本机框架窗口) .

Thank you.谢谢你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM