How to limit a sizer within a certain space? (wxWidgets) - c++

I'm using a sizer to place the buttons in a grid:
However, I don't want the sizer to use all the space in the window, but just a small area, like the following (in the white area):
Is this possible to achieve? And if yes, how?
Please know that I'm VERY new to wxWidgets.
By the way, here's the code behind the frame:
Main::Main() : wxFrame(nullptr, wxID_ANY, "IKE Calculator", wxDefaultPosition, wxSize(500, 650), wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER)
{
int w = 3, h = 3;
NumBtn = new wxButton*[w*h];
NumSizer = new wxGridSizer(w, h, 0, 0);
//Populate sizer
int cycle = 1;
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int current = (y * w + x);
NumBtn[current] = new wxButton(this, BUTTON_NUM + current, std::to_string(cycle));
NumSizer->Add(NumBtn[current], 1, wxEXPAND | wxALL);
cycle++;
}
}
this->SetSizer(NumSizer);
NumSizer->Layout();
}
Main::~Main()
{
delete[] NumBtn;
}

Sizers are used to position children of a window and they use the entire window client area (i.e. everything inside it) for this. This doesn't mean that you can't position your buttons in the way you want to do it, of course, it just means that you have to do it in the right way:
Either you can create a child window which takes just the area you want it to occupy and then you create your buttons as its children and associate the sizer with this window. This, of course, assumes you can position this window correctly, but windows have SetSize() method, so in principle this could be done. But setting sizers manually is not the right thing to do and you risk running into a common issue with positioning the only child of a window, so it's better to do it in another way.
This other way is to use "spacers", which are sizer elements that take "space", without doing anything else, to occupy the unused parts of the window. For example, in this case you could create a vertical box sizer (wxBoxSizer(wxVERTICAL)) containing a spacer of the given height (the top area) and a horizontal box sizer containing a spacer corresponding to the left gap and your current sizer.
Note that a layout such as what you want to create is rather unusual, typically you don't need to just leave arbitrarily-sized parts of the window empty. So you wouldn't do something like this often, but if you really want to, you can do it, of course.

Related

Notifying wxSizer of dynamic layout changes

I have the following hierarchy for layout:
wxMDIChildFrame -> wxNotebook ->
wxScrolledWindow -> wxBoxSizer -> wxStyledTextCtrl
GOAL: The wxStyledTextCtrl (CScriptWnd) resizes itself when user adds or deletes a line or presses Shift+Enter to add another CScriptWnd to the wxScrolledWindow.
The following code is when a line is added:
void CScriptWnd::OnNewLineAdded(wxStyledTextEvent& event)
{
long col, line;
PositionToXY(GetInsertionPoint(), &col, &line);
auto ClientSize = GetClientSize();
int Height = ClientSize.GetHeight();
Height += TextHeight(line);
SetClientSize(ClientSize.GetWidth(), Height);
SetMinSize(wxSize(ClientSize.GetWidth(), Height));
Refresh();
//m_ParentWindow->FitInside(); //CScriptWnd gets smaller rather than larger
//m_ParentWindow->Layout(); //CScriptWnd gets smaller rather than larger
//m_ParentWindow->GetGrandParent()->Layout(); //No effect
event.Skip();
}
Most work well such as the CScriptWnd resizes itself, the new script window added to wxScrolledWindow etc...
The problem is that the update to user interface only properly happens when the wxMDIChildFrame is resized using the mouse.
For example, if there are two CScriptWnd and the top one resizes itself, it overlaps with the bottom one
until the wxMDIChildFrame is resized using the mouse. Similar happens for the visibility of scrollbars such that when CScriptWnd client size gets larger the scrollbars only become visible when top-level window resized using the mouse.
Not sure what I am missing.
You probably need
SetMinSize(GetSize());
GetParent()->Layout();

Prevent wxScrolledWindow from scrolling when proportion is 0

I am trying to layout my wxWidgets window.
I have a vertical wxBoxSizer with a panel, proportion = 1, and a wxScrolledWindow, proportion = 0. I want the wxScrolledWindow's vertical height to be large enough to contain the wxButtons inside it, which are laid out horizontally.
However, with proportion = 0 a vertical scrollbar appears, even though I do not pass the wxVSCROLL flag. I tried settings proportion = 1 but this meant the wxScrolledWindow would be taller than necessary.
Here is an image of the problem, taken from inside wxFormBuilder (the same behaviour occurs in the actual wxWidgets program, where the wxScrolledWindow has proportion = 0:
Here is what I am trying to achieve:
Please could somebody explain how I could achieve this in C++ (or wxFormBuilder, it doesn't matter); I have glanced through the wxWidgets documentation but I am a novice at GUI libraries in general so assistance would be much appreciated.
If you don't want the vertical scrollbar, just disable it, otherwise the layout code doesn't really know which size is desired for your scrolled window.
Here is something that creates the layout that you want (you can paste it into the minimal sample, at the end of MyFrame ctor, to test):
auto const sv = new wxBoxSizer(wxVERTICAL);
sv->Add(new wxButton(this, wxID_ANY, "Button 0"), wxSizerFlags(1).Border());
auto const p = new wxScrolledWindow(this);
p->SetScrollRate(10, 0);
auto const sh = new wxBoxSizer(wxHORIZONTAL);
for ( int i = 0; i < 10; ++i )
sh->Add(new wxButton(p, wxID_ANY, wxString::Format("Button %d", i)), wxSizerFlags().Border());
p->SetSizer(sh);
sv->Add(p, wxSizerFlags().Expand());
SetSizer(sv);

How to position a QLabel on Qt?

I'm writing a program where I need to create a QLabel by code, instead of drag and drop, but I'm having problems positioning the QLabel as I want, this is the code I have:
if(ui->WorksList->currentItem()->text() == "Work1")
{
ui->InformationLabel->show();
QLabel *label = new QLabel(this);
label->show();
label->setText("Extraction");
label->setMinimumWidth(100);
int x = 2000;
x = label->geometry().x();
int y = 2000;
y = label->geometry().y();
}
With this piece of code my QLabel do not move from the top left corner.
Thank you
Your posted code doesn't move anything; as one of the comments said, you have to use the move method to move the label. If it doesn't show up, then it's either off screen or covered up by something else, or both. The coordinates for the move method are in the parent widget's coordinates, not the screen, and not the window. You've parented this QLabel to this, so the coordinates are relative to the top left corner of this, whatever that is.
Your code doesn't show what this is, nor any mention of how large the area of this is, so it's impossible for us to tell what coordinates might be reasonable.

Wrapping text in GTK3 treeview

I have trouble getting TreeView in GTK3 to wrap text correctly.
I set it up to wrap in this way:
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_mode().set_value(Pango::WRAP_WORD_CHAR);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(200);
This works, text is wrapped, but when I resize the window and make it bigger, there is a lot of ugly white-space above and below cell with long text. It seems, that GTK reserves height for cell based on wrap width. Which makes no sense to me.
I tried to get around with setting needed in signal_check_resize with calculating needed width like this:
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
auto width = this->get_allocated_width()
- mTreeView.get_column(0)->get_width()
- mTreeView.get_column(1)->get_width();
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(width-100);
this->forceRecreateModel = true; //Needed to work
But this lets me only make window bigger. It cannot be shrinked, after it was resized.
The question is, how this is properly done?
I am using gtk3.20.3-1 and gtkmm3.20.1-1 on Arch linux.
EDIT: fixed typo in the title...
In the end I found how to do it.
In the setup of the window (for me constructor of the window derived class) it was necessary to set column to be AUTOSIZE in order to allow shrinking of the width.
//Last Column setup
{
mTreeView.append_column("Translation", mColumns.mEnglish);
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
pColumn->set_sizing(Gtk::TreeViewColumnSizing::TREE_VIEW_COLUMN_AUTOSIZE);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_mode().set_value(Pango::WRAP_WORD_CHAR);
}
Also there is needed to set correct wrap width on every resize. Without this, height of the row was as big as it would be necessary for currently set wrap_width with no regard on current width (resulting in big padding on the top, when stretched more and prohibiting to make window smaller).
This code was also in the constructor.
this->signal_check_resize().connect([this]()
{
//calculate remaining size
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
auto width = this->get_allocated_width()
- mTreeView.get_column(0)->get_width()
- mTreeView.get_column(1)->get_width()-30;
//minimum reasonable size for column
if(width < 150)
width = 150;
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(width);
//debounce
static auto oldsize = 0;
{
oldsize = width;
//trigger redraw of mTreeView (by clearing and refilling Model,
//it is done in 100ms pulse)
this->mRedrawNeeded = true;
}
});
And maybe it is worth noting, that I have mTreeView encapsulated in Gtk::ScrolledWindow. So this is a chunk which comes before column setup. :)
//in class is: Gtk::ScrolledWindow mScrollForResults;
//scrolling area
mGrid.attach(mScrollForResults, 0,2,10,1);
mScrollForResults.set_hexpand();
mScrollForResults.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC,
Gtk::PolicyType::POLICY_ALWAYS);
mScrollForResults.set_margin_top(10);
mScrollForResults.set_min_content_width(400);
mScrollForResults.set_min_content_height(200);
mScrollForResults.add(mTreeView);
//results treeView
mRefListStore = Gtk::ListStore::create(mColumns);
mTreeView.set_model(mRefListStore);
mTreeView.set_hexpand();
mTreeView.set_vexpand();

Make a QDialog appear in a different screen

The title says it pretty much all:
I have two screens, and each time I create a QDialog it appears in the same screen as its parent.
How can I make it appear in a different screen? Or should I use a different type of top-level widget?
The code I use to create the dialog is:
QDialog my_dialog = new QDialog(this,
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint);
...
EDIT:
I have also tried using the QDesktopWidget which gives me a QScreen object that refers to the second screen. But then I don't find how to instruct the QDialog to use that QScreen (setting it as the parent doesn't work).
It is bad, that you edit your question without reading comments :(
// Your screen geometry:
QRect buildScreenGeometry()
{
auto desktop = QApplication::desktop();
QRect virtualRect;
const auto n = desktop->screenCount();
for ( auto i = 0; i < n; i++ )
virtualRect |= desktop->screenGeometry(i);
return virtualRect;
}
// Moving
auto dlg = new QDialog( someParent );
auto newPoint = QPoint( 2000, 0 ); // point on another screen
auto realPos = someParent->mapFromGlobal( newPoint );
dlg->move( realPos );
That's all.
UPDATE:
You should understand, that there are only ONE screen area with COMMON coordinate system, that contains ALL screens.
For example, you have 2 monitors with 800x600 resolution. First (main) monitor is standing left, and second standing right. In this case, coordinate system, that is available for your application is 1600x600. So, if your widget has 100x100 top-left position, on a first monitor, and you want to move it to another, you should call move(900x100); // 900 == screen1.width() + dialog.pos().x(). Then your widget will have 100x100 position on second monitor.
You should read Qt documentation.
You can use move on your QDialog, but be aware that move will set the QDialog position relative to it's parent.
You can get the Main Window's screen position and use that to setup the QDialog position. Just know that you're not guaranteed to have 2 screens on the end user machine.
For more information on move see: http://doc.qt.io/qt-5/application-windows.html#window-geometry