wxTextCtrl resizing issue - c++

I'm creating a wxWidgets C++ calculator application. I have a series of wxTextCtrl's for the main expression text and other stuff. They are, of course, positioned using sizers so that they grow larger or smaller as I resize the window. However, if I maximize the window, their size successfully updates, but not if I then minimize it.
My app in a normal state:
Maximized application:
And when I minimize it back:
The wxTextCtrl's sizes eventually update and correct themselves when I resize the window, but this bug is kind of annoying.
Here's the code where these controls are defined:
text_controls.cpp
#include "main.h"
void Main::AddTextControls()
{
auto sizer = new wxBoxSizer(wxVERTICAL);
Chronology = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RIGHT);
Chronology->SetBackgroundColour(wxColour(235, 235, 235));
Chronology->SetForegroundColour(wxColour(105, 105, 105));
Chronology->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
Chronology->SetFont(
wxFontInfo(wxSize(0, Chronology->GetSize().y / 1.5))
.Family(wxFONTFAMILY_SWISS)
.FaceName("Calibri Light")
);
});
sizer->Add(Chronology, 4, wxEXPAND);
Expression = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RIGHT);
Expression->SetForegroundColour(wxColour(55, 55, 55));
Expression->Bind(wxEVT_TEXT, &Main::OnTextChange, this);
Expression->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
Expression->SetFont(
wxFontInfo(wxSize(0, Expression->GetSize().y / 1.3))
.Family(wxFONTFAMILY_SWISS)
.FaceName("Calibri")
.Bold()
);
});
sizer->Add(Expression, 10, wxEXPAND);
//memoria
auto memSizer = new wxBoxSizer(wxHORIZONTAL);
MemText = new wxTextCtrl(this, wxID_ANY, "Memoria", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
MemText->SetBackgroundColour(wxColour(55, 55, 55));
MemText->SetForegroundColour(wxColour(119, 119, 119));
MemText->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
MemText->SetFont(
wxFontInfo(wxSize(0, MemText->GetSize().y / 1.3))
.Family(wxFONTFAMILY_SWISS)
.FaceName("Corbel")
);
});
memSizer->Add(MemText, 25, wxEXPAND);
Memory = new wxTextCtrl(this, wxID_ANY, "0", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RIGHT);
Memory->SetBackgroundColour(wxColour(55, 55, 55));
Memory->SetForegroundColour(wxColour(105, 105, 105));
Memory->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
Memory->SetFont(
wxFontInfo(wxSize(0, Memory->GetSize().y / 1.3))
.Family(wxFONTFAMILY_SWISS)
.FaceName("Calibri Light")
);
});
memSizer->Add(Memory, 100, wxEXPAND);
sizer->Add(memSizer, 3, wxEXPAND);
MainSizer->Add(sizer, 30, wxEXPAND);
}
Anybody got a solution? Also, I'm very new to wxWidgets.
EDIT:
Followed a suggestion and added this snippet to my main frame constructor:
Main::Main() : wxFrame(nullptr, wxID_ANY, "IKE Calculator", wxDefaultPosition, wxSize(525, 650), wxDEFAULT_FRAME_STYLE)
{
this->SetMinSize(wxSize(325, 450));
FrameSize = this->GetSize();
MainSizer = new wxBoxSizer(wxVERTICAL);
this->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
CallAfter(Layout()); //will be called later
});
this->SetBackgroundColour(wxColour(70, 73, 101));
AddTextControls();
AddMainButtons();
AddButtons();
this->SetSizer(MainSizer);
MainSizer->Layout();
}
Getting a C2064 error in VS2019: The term doesn't return a function that accepts 0 arguments.
EDIT:
Changed CallAfter(Layout()); to CallAfter(&Main::Layout); and I'm getting the same error.

Let me reword your description:
"When I change the size of the window, manually or by maximizing it, everything goes well. But when I restore the window from a maximized status then fonts are wrongly updated"
If fonts must be changed accordingly with the size of the window then the size-event must be catched in the control. The handler gets a wxSizeEvent object with the new size of the control.
==>> So use this new size (i.e. evt.GetSize()) instead of control.GetSize() in your control->Bind(wxEVT_SIZE...) lambdas.
The issue comes from the fact that wxSizer's layout-process uses the current font in the control to calculate the new size. Thus, when you receive the size-event is too late to be taken the new font-size into account.
So, when the window is resized again, with a more or less same size as the last one, then old font size is the same as the new one, and then everything matches.
What can you do?
Catch the size event for the whole window (still use your handlers for changing the font) and call 'Layout()' after the handler finishes:
MyFrame->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) {
evt.Skip();
CallAfter(Layout()); //will be called later
});
This second 'Layout` (first one is fired in sizer's code) may show a bit of flicker.

Related

wxTE_MULTILINE flag breaks wxTextCtrl font scaling

I'm building an app using wxWidgets. I have a wxTextCtrl created like so:
Expression = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxNO_BORDER | wxTE_RIGHT );
Expression->SetForegroundColour(wxColour(55, 55, 55));
Expression->Bind(wxEVT_TEXT, [this](wxCommandEvent& evt) { Expression_Update(); evt.Skip(); });
Expression->Bind(wxEVT_SIZE, [this](wxSizeEvent& evt) { Expression_Update(); evt.Skip(); });
sizer->Add(Expression, 10, wxEXPAND);
It is bind to an Expression_Update() function which is supposed to scale its font correctly.
void Main::Expression_Update()
{
wxString text = Expression->GetValue();
const int MaxWidth = GetSize().GetWidth();
int X, Y = Expression->GetSize().GetHeight();
wxFont* font = new wxFont(
wxSize(wxSize(0, Y / 1.3)),
wxFONTFAMILY_SWISS,
wxFONTSTYLE_NORMAL,
wxFONTWEIGHT_BOLD,
false,
"Calibri"
);
Expression->GetTextExtent(text, &X, nullptr, NULL, NULL, font);
if ((X + EXP_ANTI_CLIPPING_COEF) > MaxWidth)
{
do
{
int const fontHeight = font->GetPixelSize().GetHeight();
font->SetPixelSize(wxSize(0, fontHeight - 2));
Expression->GetTextExtent(text, &X, nullptr, NULL, NULL, font);
} while ((X + EXP_ANTI_CLIPPING_COEF) > MaxWidth);
}
Expression->SetFont(*font);
delete font;
}
This code works perfectly fine. However, if I add the wxTE_MULTILINE flag to the constructor:
Expression = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxNO_BORDER | wxTE_RIGHT | wxTE_MULTILINE );
...when I resize the window, the wxTextCtrl grows larger and larger until it fills up the entire frame.
I did some debugging myself, and I figured out the problem:
int X, Y = Expression->GetSize().GetHeight();
Without the wxTE_MULTILINE flag, Y has a starting value of 88 and changes correctly based on the control's height when I resize. But with the multiline flag set, Y starts with 88, and when I resize the window, regardless if the window height increased or not, the value changes to 164, and when I resize again, to 308, and so on.
TLDR: The GetSize().GetHeight() method returns an incorrect height value that causes the font to grow larger and larger until the text control fills up the entire window.

Scaling font as window is resized

I'm writing a C++ calculator application in wxWidgets.
I want the font of all the buttons and the two wxTextCtrl's to be scaled as I resize the window. How is that done? I'm posting some code if that can help.
text_controls.cpp
#include "main.h"
void Main::AddTextControls()
{
//creazione font
wxFont Expression(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT, false, "Lato");
wxFont Main(30, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT, false, "Lato");
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
ExpText = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
ExpText->SetFont(Expression);
ExpText->SetForegroundColour(wxColour(125, 125, 125));
sizer->Add(ExpText, 1, wxEXPAND);
MainText = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
MainText->SetFont(Main);
MainText->SetForegroundColour(wxColour(55, 55, 55));
MainText->Bind(wxEVT_TEXT, &Main::OnTextChange, this);
sizer->Add(MainText, 2, wxEXPAND);
MainSizer->Add(sizer, 4, wxEXPAND);
}
You could use wxFont::SetPixelSize() to set the font size to, say, one third of the text control height. E.g.:
MainText->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) {
e.Skip();
MainText->SetFont(wxFontInfo(wxSize(0, MainText->GetSize().y / 3)).Family(wxFONTFAMILY_SWISS).FaceName("Lato").Light());
});
(which also shows using a more readable way of creating a wxFont than the ctor taking a zillion arguments).

How to resize frame after hiding a component in wxwidgets

I have a frame which has 4 textboxes and one link(change no of textboxes). when user clicks on link I am hiding 4th textbox. but what happens is after hiding 4th textbox, it's space remains blank. I don't want to keep its space blank. That space should disappear and frame should get resized. Is there any way that I can achieve this? when user again clicks on the link(change no of textboxes I am again displaying 4th textbox.
code:
m_ComputerNameText1 = new wxTextCtrl(m_panel, wxID_ANY, "computerName", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText2 = new wxTextCtrl(m_panel, wxID_ANY, "computerName1", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText3 = new wxTextCtrl(m_panel, wxID_ANY, "computerName3", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_ComputerNameText4 = new wxTextCtrl(m_panel, wxID_ANY, "computerName4", wxDefaultPosition, wxSize(250, wxDefaultSize.GetHeight()));
m_hyperLinkOption = new wxHyperlinkCtrl(m_panel, wxID_ANY, "Change no of textboxes", wxT(""), wxDefaultPosition, wxSize(135, wxDefaultSize.GetHeight()));
m_userPassSizer->Add(m_ComputerNameText1, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText2, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText3, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_ComputerNameText4, 0, wxALIGN_LEFT | wxALL, 0);
m_userPassSizer->Add(m_hyperLinkOption , 0, wxALIGN_LEFT | wxALL, 0);
Following function executes when hyperlink is clicked.
void OnAuthCodeOptionLinkClicked(wxHyperlinkEvent& event)
{
if (!m_hyperlinkOptionSelected)
{
m_hyperlinkOptionSelected= true;
m_ComputerNameText4->Hide();
m_hyperLinkOption->SetLabel("Go back");
}
else
{
m_hyperlinkOptionSelected= false;
m_ComputerNameText4->Show();
m_hyperLinkOption->SetLabel("change no of textboxes");
}
}
in this way I have to hide and show 4th textbox. After hiding some blank space remains. How can I avaoid that blank space and take link in the place of 4th textbox ?
Whenever I need to dynamically update the size of a control, I always call Layout() and Refresh().
You can find more info about Layout here:
http://docs.wxwidgets.org/3.1/classwx_window.html#a1b143c3e72bd0af533b76db4830a6113
As well as some good info on window sizing in general here:
http://docs.wxwidgets.org/3.1/overview_windowsizing.html
Calling Layout assumes that your class where you construct and manage your textboxes, etc. is a wxWindow derived control itself (like a panel, frame, whatever). If not, like you are making the changes on a view-model, you will need to call Layout and Refresh on the owning window:
void UpdateView()
{
view->Layout();
view->Refresh();
}
You may need to play around with your sizers, minimum size of windows, etc. to get the exact behavior you want.

Spacer added to inner wxBoxSizer doesn't work in wxWidgets

I have an issue with spacer inside of the inner wxBoxSizer.
Here is the sample code.
class Frame : public wxFrame
{
public:
Frame()
: wxFrame(nullptr,
wxID_ANY,
wxEmptyString,
wxDefaultPosition,
wxSize(600, 200))
{
wxPanel* panel = new wxPanel(this);
// Some OUTER SIZER
wxBoxSizer* mainS = new wxBoxSizer(wxVERTICAL);
// INNER HORIZONTAL SIZER
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* text = new wxStaticText(panel, wxID_ANY, wxT("Some Text"));
sizer->Add(text, 0, wxALL, 5);
wxComboBox* comboBox = new wxComboBox(panel, wxID_ANY, wxT("Combo"));
sizer->Add(comboBox, 0, wxALL, 5);
// SPACER
sizer->Add(0, 0, 1, wxEXPAND, 5);
wxButton* button = new wxButton(panel, wxID_ANY, wxT("Some button"));
sizer->Add(button, 0, wxALL, 5);
mainS->Add(sizer, 0, wxALL, 5);
panel->SetSizer(mainS);
// PANEL SIZER
wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
panelSizer->Add(panel, 1, wxEXPAND, 5);
SetSizer(panelSizer);
Layout();
Centre(wxBOTH);
}
};
class WxguiApp
: public wxApp
{
public:
bool OnInit() override
{
Frame* w = new Frame();
w->Show();
SetTopWindow(w);
return true;
}
};
IMPLEMENT_APP(WxguiApp);
If I remove outer sizer spacer starts working fine. Is it bug in my code? Or maybe some issue with wxWidgets?
I couldn't find answer, maybe someone help? wxWidgets 3.0.2
Your inner sizer (sizer) won't expand in the horizontal direction because you didn't tell its parent sizer (mainS) to do it when adding it, so your spacer will always be 0 pixels wide, which is its initial and minimal width.
To fix this, you just need to change the mainS->Add(sizer, 0, wxALL, 5) line to use wxALL | wxEXPAND.
Additionally, I strongly encourage you to look at using wxSizerFlags instead of the cryptic syntax shown above. And, last and really least, but still worth mentioning, prefer using much more readable AddStretchSpacer() to the confusing Add(0, 0, 1, wxEXPAND, 5) call (it's confusing because expanding your spacer doesn't do anything and border size is not taken into account without any border flags).

GUI layout with wxSizer, wxSashWindow

I'm trying to create a window which is splitted into 2, 3, 4 etcc different resizable views in mainwindow and I'd like to implement that with wxWidgets in C::B. Actually th canvas itself splits the windows in the requested numbers, but it doesn't place any sash alongn the border of views so that it's very diffciult to notice which view starts where and ends where.
I create everything on run time and I was planing to place the sash, or panels around borders of views and hoping to work in my case but the main frame is reluctant to place the panels, and sashwidnwos at the correct position and stops processing the OnSize event. That is previously working codes is not functioning properly if I add the below code with sizers and panels during resizing.
MainFrame *frame = new MainFrame(NULL, wxT("wxWidgets OSG Sample"),
wxDefaultPosition, wxSize(width, height));
wxToolBar* toolbar = new wxToolBar(frame, wxID_ANY,wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL);
//wxSashLayoutWindow* win = new wxSashLayoutWindow(frame, ID_WINDOW_TOP,wxPoint(50,10), wxSize(200, 30),
// wxSW_3D | wxCLIP_CHILDREN);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxPanel* panel = new wxPanel(frame, ID_TBbutton, wxPoint(300,0), wxSize(5,500));
panel->SetWindowStyle(wxDOUBLE_BORDER);
// panel->SetStyle(wx_3D)
wxPanel* panel1 = new wxPanel(frame, ID_TBbutton);
sizer->Add( panel, 0, wxALL, 0 );
frame->SetSizer(sizer);
frame->SetToolBar(toolbar);
Please find below very basic example of MDI form with wxSashLayoutWindow:
bool SashWindowTestApp::OnInit()
{
wxMDIParentFrame* mainFrame = new wxMDIParentFrame(NULL, ID_TEST_FRAME,
wxT("Sash window test"), wxPoint(0, 0), wxSize(500, 400),
wxDEFAULT_FRAME_STYLE);
// top window
wxSashLayoutWindow* topWindow = new wxSashLayoutWindow(mainFrame,
ID_WINDOW_TOP, wxDefaultPosition, wxSize(200, 100),
wxSW_3D);
topWindow->SetDefaultSize(wxSize(1000, 100));
topWindow->SetAlignment(wxLAYOUT_TOP);
topWindow->SetBackgroundColour(*wxGREEN);
topWindow->SetSashVisible(wxSASH_BOTTOM, true);
// bottom window
wxSashLayoutWindow* bottomWindow = new wxSashLayoutWindow(mainFrame,
ID_WINDOW_BOTTOM, wxDefaultPosition, wxSize(200, 200),
wxSW_3D);
bottomWindow->SetDefaultSize(wxSize(1000, 200));
bottomWindow->SetAlignment(wxLAYOUT_BOTTOM);
bottomWindow->SetBackgroundColour(*wxYELLOW);
bottomWindow->SetSashVisible(wxSASH_TOP, true);
wxLayoutAlgorithm layout;
layout.LayoutMDIFrame(mainFrame);
mainFrame->Show(true);
return true;
}
In order to have a fully working sash window your frame has to react on EVT_SASH_DRAGGED