字体大小缩放问题

Font size scaling problems

我正在编写一个 C++ wxWidgets 计算器应用程序,并且我希望我的 wxTextCtrl 和自定义按钮的字体在我调整 window.

大小时缩放

问题是:

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

我正在使用此代码:

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()
        );
    });

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

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()));
}

对于第二个问题,我真的不知道该怎么办。我认为当框架未最大化时从大小事件设置字体大小会导致某些事情以意外的顺序完成并暂时破坏 wxWidgets 的布局系统。

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

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

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

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

然后为 MSWHandleMessage

添加 body
#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' 更改为您的框架名称 class。


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

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

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

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

请随意跳过下一部分。

通过基于 font metric named ascent instead of the text height. Here's a diagram of what these font metrics mean from Microsoft

的计算,您有时可以更好地使文本垂直居中

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

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