【问题标题】:Font size scaling problems字体大小缩放问题
【发布时间】:2021-08-22 21:09:39
【问题描述】:

我正在编写一个 C++ wxWidgets 计算器应用程序,我希望我的 wxTextCtrl 的字体和我的自定义按钮在我调整窗口大小时进行缩放。

问题是:

  1. 我的按钮中的文本并不总是准确地位于中心,但有时会略微偏离(尤其是在绿色和红色按钮中)
  2. 当我最大化窗口时,wxTextCtrl 的字体大小会更新,但当我最小化它时不会更新,让它覆盖一半屏幕,直到我调整窗口大小,此时它会更新到正确的大小。

我正在使用此代码:

text_controls.cpp

    MainText = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
    MainText->SetForegroundColour(wxColour(55, 55, 55));
    MainText->Bind(wxEVT_TEXT, &Main::OnTextChange, this);
    MainText->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
        evt.Skip();
        MainText->SetFont(
            wxFontInfo(wxSize(0, MainText->GetSize().y / 1.3))
                .Family(wxFONTFAMILY_SWISS)
                .FaceName("Lato")
                .Bold()
        );
    });

我使用非常相似的代码在我的自定义按钮类文件中生成字体:

ikeButton.cpp

void ikeButton::render(wxDC& dc)
{
    unsigned int w = this->GetSize().GetWidth();
    unsigned int h = this->GetSize().GetHeight();
    wxColour* bCol;

    if (pressed) {
        dc.SetBrush(*pressedBackgroundColor);
        dc.SetTextForeground(*pressedTextColor);
        bCol = pressedBorderColor;
    }
    else if (hovered) {
        dc.SetBrush(*hoveredBackgroundColor);
        dc.SetTextForeground(*hoveredTextColor);
        bCol = hoveredBorderColor;
    }
    else {
        dc.SetBrush(*backgroundColor);
        dc.SetTextForeground(*textColor);
        bCol = borderColor;
    }
    
    dc.SetPen(*wxTRANSPARENT_PEN);
    dc.DrawRectangle(0, 0, w, h);

    //bordo
    if (borderTh && bCol != NULL)
    {
        dc.SetBrush(*bCol);
        dc.DrawRectangle(0, 0, w, borderTh);
        dc.DrawRectangle(w - borderTh, 0, borderTh, h);
        dc.DrawRectangle(0, h - borderTh, w, borderTh);
        dc.DrawRectangle(0, 0, borderTh, h);
    }

    //testo
    dc.SetFont(
        wxFontInfo(wxSize(0, this->GetSize().GetHeight() / 3))
        .Family(wxFONTFAMILY_SWISS)
        .FaceName("Lato")
        .Light()
    );
    dc.DrawText(text, w / 2 - (GetTextExtent(text).GetWidth()),
        h / 2 - (GetTextExtent(text).GetHeight()));
}

【问题讨论】:

    标签: c++ fonts wxwidgets


    【解决方案1】:

    我真的不确定如何处理第二个问题。我认为当框架未最大化时从 size 事件设置字体大小会导致某些事情以意外的顺序完成并暂时破坏 wxWidgets 的布局系统。

    到目前为止,我发现解决此问题的唯一方法是在窗口恢复时使用 WinAPI 调用注入额外的 Layout 调用。为此,请将此声明添加到您的框架类中:

    #ifdef __WXMSW__
        bool MSWHandleMessage(WXLRESULT *result,WXUINT message,
                              WXWPARAM wParam, WXLPARAM lParam) override;
    #endif // __WXMSW__
    

    MSWHandleMessage 方法的主体将需要一些在 wrapwin.h 标头中定义的额外常量。因此,在框架的代码文件中,将其设为最后一行 #include

    #ifdef __WXMSW__
        #include <wx/msw/wrapwin.h>
    #endif // __WXMSW__
    

    然后为MSWHandleMessage添加这个正文

    #ifdef __WXMSW__
        bool MyFrame::MSWHandleMessage(WXLRESULT *result, WXUINT message,
                                       WXWPARAM wParam, WXLPARAM lParam)
        {
            if ( message == WM_SYSCOMMAND && wParam == SC_RESTORE )
            {
                CallAfter([this](){Layout();});
                // We still want to do the default processing, so do not set a result
                // and return true.
            }
    
            return wxFrame::MSWHandleMessage(result, message, wParam, lParam);
        }
    #endif // __WXMSW__
    

    显然将“MyFrame”更改为您的框架类的名称。


    但我可以帮助解决第一个问题。

    我认为当前在哪里绘制文本的计算存在两个小问题。首先,我认为应该在直流而不是窗口上调用GetTextExtent。其次,我认为数学运算的顺序有一点问题。为了使文本居中,我认为 x 坐标的计算应该是(w - GetTextExtent(text).GetWidth()) / 2。在计算 y 坐标时也应进行类似的更改。

    我还会存储文本范围计算而不是两次:

    wxSize textExtent = dc.GetTextExtent(text);
    dc.DrawText(text, (w - textExtent.GetWidth())/ 2,
                (h - textExtent.GetHeight())/2);
    

    请随意跳过下一部分。

    您有时可以通过基于名为 ascent 的 font metric 而不是文本高度的计算来更好地垂直居中文本。这是Microsoft中这些字体指标含义的图表

    ascent 可能是一个更好的使用数字的原因是高度将包含几个填充元素,这可能会使文本看起来有点不居中。

    wxSize textExtent = dc.GetTextExtent(text);
    wxFontMetrics metrics = dc.GetFontMetrics();
    dc.DrawText(text, (w - textExtent.GetWidth()) / 2, (h - metrics.ascent) / 2);
    

    【讨论】:

    • 第一个问题的解决方法是检查窗口是否最小化,在这种情况下不更改字体。
    • 我相信他们实际上意味着当按下恢复按钮而不是最小化按钮时会发生问题。当按下恢复按钮时,大小事件会破坏布局并使文本控件占用太多空间。我使用CallAfter 尝试了一些解决方法,但我没有找到任何可行的方法。
    • 我已添加到答案中,显示了使用本机 WinAPI 调用的解决方法。必须有更好的方法来做到这一点,但我还没有找到。
    • 可以添加一个检查窗口没有最小化到渲染代码的帮助吗?我不确定这里发生的事情的顺序,但看起来render() 可以在大小更新之前被调用?最好将调试打印添加到代码中以查看到底发生了什么。或者,或者,拥有一个允许在此处轻松调试的 SSCCE。
    猜你喜欢
    • 2014-02-03
    • 2020-05-08
    • 2023-03-22
    • 2013-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多