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).
Related
I created a global panel and called a method that creates a sizer and a button. The button clears the sizer (i.e., also the panel), and then deletes is. Then, another method is called, using the same logic, it creates another sizer and another button. This time they don't work.
my code(windows, vs studio):
#include "MainFrame.h"
#include <wx/wx.h>s
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title)
{
panel = new wxPanel(this);
StartParty(panel);
}
void MainFrame::ClearButtonClicked(wxCommandEvent& evt)
{
panel->GetSizer()->Clear(true);
panel->SetSizerAndFit(nullptr);
ChooseMode(panel);
}
void MainFrame::StartParty(wxPanel* parent)
{
wxButton* start_button = new wxButton(parent, wxID_ANY, "Start the Party!", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
start_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizerAndFit(sizer);
start_button->Bind(wxEVT_BUTTON, &MainFrame::ClearButtonClicked, this);
}
void MainFrame::ChooseMode(wxPanel* parent)
{
wxButton* select_button = new wxButton(parent, wxID_ANY, "Choose", wxDefaultPosition, wxSize(200, 70));
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->AddStretchSpacer(1);
sizer->Add(
select_button,
0,
wxALL | wxALIGN_CENTER,
0);
sizer->AddStretchSpacer(1);
parent->SetSizer(sizer);
}
You do need Layout(), as mentioned in the comments, as this is what actually repositions the windows -- SetSizer() just specifies the sizer to use for doing it, but doesn't do anything on its own immediately (it will when the window is resized the next time, as this results in a call to Layout()).
However, even if it's somewhat unrelated to the question itself, I think you shouldn't be doing this at all and use wxSimplebook instead. This simple (sic) class allows you to add a few pages to it and then easily switch between them.
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.
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.
My wxWidgets program should work on both desktop PC and Windows tablet. Both use Windows 10 Pro 64-bit version. How can I make it, that a text with a changing size is inside a dialog no matter where it is displayed?
This is what I do now:
TrackDialog::TrackDialog(wxWindow* parent,wxWindowID id,const wxPoint& pos,
const wxSize& size)
{
//(*Initialize(TrackDialog)
wxBoxSizer* BoxSizer4;
wxBoxSizer* BoxSizer5;
wxBoxSizer* BoxSizer2;
wxBoxSizer* BoxSizer1;
wxBoxSizer* BoxSizer3;
wxBoxSizer* pPanelSizer;
Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSTAY_ON_TOP|wxSUNKEN_BORDER, _T("wxID_ANY"));
Panel1 = new wxPanel(this, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
pPanelSizer = new wxBoxSizer(wxVERTICAL);
BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
StaticSelectText = new wxStaticText(Panel1, ID_STATICTEXT1, _("Select a *.xml file."), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
BoxSizer1->Add(StaticSelectText, 1, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
pPanelSizer->Add(BoxSizer1, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
BoxSizer2 = new wxBoxSizer(wxHORIZONTAL);
XmlFilePickerCtrl = new wxFilePickerCtrl(Panel1, ID_FILEPICKERCTRL1, _T("./resources/"), wxEmptyString, _T("*.xml"), wxDefaultPosition, wxDefaultSize, wxFLP_FILE_MUST_EXIST|wxFLP_OPEN|wxFLP_USE_TEXTCTRL, wxDefaultValidator, _T("ID_FILEPICKERCTRL1"));
BoxSizer2->Add(XmlFilePickerCtrl, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
pPanelSizer->Add(BoxSizer2, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
BoxSizer3 = new wxBoxSizer(wxHORIZONTAL);
StaticNumberText = new wxStaticText(Panel1, ID_STATICTEXT2, _("Tracknumber \n"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT2"));
StaticNumberText->Disable();
wxFont StaticNumberTextFont(10,wxSWISS,wxFONTSTYLE_NORMAL,wxNORMAL,false,_T("Arial"),wxFONTENCODING_DEFAULT);
StaticNumberText->SetFont(StaticNumberTextFont);
BoxSizer3->Add(StaticNumberText, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
pPanelSizer->Add(BoxSizer3, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
BoxSizer4 = new wxBoxSizer(wxHORIZONTAL);
ChoiceTrack = new wxChoice(Panel1, ID_CHOICE1, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE1"));
ChoiceTrack->Disable();
BoxSizer4->Add(ChoiceTrack, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
pPanelSizer->Add(BoxSizer4, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
BoxSizer5 = new wxBoxSizer(wxHORIZONTAL);
ButtonOk = new wxButton(Panel1, ID_BUTTON1, _("OK"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
BoxSizer5->Add(ButtonOk, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
pPanelSizer->Add(BoxSizer5, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
Panel1->SetSizer(pPanelSizer);
pPanelSizer->Fit(Panel1);
pPanelSizer->SetSizeHints(Panel1);
Center();
Connect(ID_FILEPICKERCTRL1,wxEVT_COMMAND_FILEPICKER_CHANGED,(wxObjectEventFunction)&TrackDialog::OnXmlFilePickerCtrlFileChanged);
Connect(ID_CHOICE1,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&TrackDialog::OnChoiceTrackSelect1);
Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&TrackDialog::OnButtonOkClick);
//*)
SetSize(DoGetBestClientSize());
Fit();
}
The text is changed from "Track number" to one of the options below:
void TrackDialog::setOutputAndChoice()
{
std::vector<wxString> storedTracks = m_xmlReader.getNamesOfTracks(m_path);
int numberOfTracks = storedTracks.size();
StaticNumberText->Enable();
wxString text;
// Make sure there are tracks stored in the file.
if(numberOfTracks > 0){
text = "There are ";
text += wxString::Format(wxT("%i"), numberOfTracks);
text += " tracks stored in the selected file. Select one."
"\nTrack 1 is the inner track.";
}
else{
text = "Opening file failed. Please chose another"
"file of the right format.";
}
StaticNumberText->SetLabel(text);
}
And this is what it looks like on desktop PC (on the tablet it looks the same, thanks to SetSize(DoGetBestClientSize());)
What can I do so that all of the text is displayed? I already tried to set it in the constructor before I call SetSizerAndFit() but then my program crashes if I want to close it...
Thank you for your help.
Edit: I put a panel in the sizer for the StaticNumberText and the text in it and I am able to display all of the text now, though now there is some nasty space underneath the StaticText:
There has to be a better way to properly layout dialogs with changing text...
Sizers need an action to get into game. User changing the size of the main (or dialog) window is definitely an action.
You can also trigger the action asking the dialog window to fit into its children. Call Fit() at the end of your TrackDialog::setOutputAndChoice()
Maybe the sizer for the StaticText there be inside the sizer for both TextCtrl and Button. Then the sizer for the StaticText will take the width of the TextCtrl.
Better, put the sizer for the StaticText, below and independently of the previous sizer.
The proportion of the StaticText, and the Panel in where is inside, must be set at 0.
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).