ButtonBar: Same Button Height - javafx

I have an app with a very dynamic button bar of about one to twelve or so buttons that change text and functionality with the current screen and/or selected record. I'm using a ButtonBar to display them.
The buttons in the bar wrap their text, when it's too large, as they should, but I end up with this dumb situation:
The buttons are different heights! I would like the ButtonBar to give me well-regulated buttons, like so:
I can cheat, after a fashion by splitting each button up into three lines. This creates a situation where the small buttons are swimming in space:
(If I use two lines, I get less wasted space, which is good, but the single-line text ceases to be centered, which also looks awful.)
Right now, the only thing I can think of to do is, after the bar renders, scan across all the buttons for the tallest one, and then adjust every other button accordingly. The ButtonBar does with width, which is good (but actually probably less desirable than all the buttons being the same height). The misleadingly named "UniformButtonSize" does this.
I'm wondering if I can do this with CSS somehow, maybe setting button heights as a percentage. Any suggestions would be appreciated!

This is a little tacky but it seems to do the trick:
public void adjustButtons() {
var tallest = 0.0;
for (Node n : buttonBar.getButtons()) {
var ht = ((Button) n).getHeight();
if (tallest < ht) tallest = ht;
}
for (Node n : buttonBar.getButtons()) {
((Button) n).setMinHeight(tallest);
}
}
So, any time the buttons in the button bar change, you fire this, it finds the tallest button, then it goes and sets all the buttons to that height.
Comments?

Related

I think recursion will solve this; but I'm not sure how?

I'm creating a UI. Right now I'm working on generic boxs that dynamically add and remove scrollbars as they are resized.
I draw the scrollbars along the bottom and right inside edge. The inside edge is important.
So at some point the box is resized. If it's resized I check to see if the new size is smaller than the size needed for the contents, if so, scroll bars are added.
My problem stems from when only one scrollbar is needed; and the size for the other dimensions/axis is very close to the size needed for the contents.
What happens is that this scrollbar is drawn on the inside edge. Now that removes area that can be used for the contents. The problem is then that if a scrollbar is shown I would have to check the area available again to see if the other scrollbar is needed. And then again.
This seems like a recursive problem; but I'm not sure how it will help me.
Some code:
void Box::UpdateVisibleRegion()
{
// if the dimensions of this box is smaller than what the widget needs, display horizontal scrollbar
if(GetDimensions().x < m_widget->GetDimensions().x)
{
m_scrollbarH->SetVisibility(true);
m_scrollbarH->SetSize(utils::coord<int>(GetDimensions().x,m_scrollbarH->GetDimensions().y));
}
else
{
m_scrollbarH->SetVisibility(false);
}
// likewise for vertical scrollbar
if(GetDimensions().y < m_widget->GetDimensions().y)
{
m_scrollbarV->SetVisibility(true);
m_scrollbarV->SetSize(utils::coord<int>(m_scrollbarV->GetDimensions().x,GetDimensions().y));
}
else
{
m_scrollbarV->SetVisibility(false);
}
And there's the problem. If the height of the box is just over the widgets height; and the width is far under it; then the horizontal scrollbar is displayed. But now the check for vertical scroll bar should have been:
if(GetDimensions().y - m_scrollbarH->GetDimensions().y < m_widget->GetDimensions().y)
Because the scrollbar is drawn on the inside edge, there's less room. But obviously I can't know that until I've checked the other. And both the horizontal and vertical are going to affect each other.
I'm sure recursion is the answer; but I can't see it.
I could probably do this with one or two big nested if statements...

How can I exchange an item and a spacer?

In qt, I have a form that contains among other things, a group with
A combo box
a checkbox
a spacer
a button
Based on some logic, I want sometimes to show another combo box... Where the spacer is, but smaller.
When I add it though, everything resizes automatically
I don't see a way to make it invisible, and yet keep items of the same size when I make it visible again.
I tried making it fixed size... But unless I use fixed sizes and positioning for everything, which I think is a bad idea, the items still move around when I change visibility.
It seems silly... But how can I make my little combo box show up instead of the spacer not next to it ? Spacers don't seem to have a name...
I would do
combo.setVisible(condition);
Spacer.setVisible(!condition);
Very easy... Except how do I access the spacer from code ?
My suggestion is to use a container QWidget instead of the spacer. Here is how it will look:
A combo box
a checkbox
a widget-container
a button
Widget-container is a QWidget with fixed size. Put your combo-box there and it will maintain it's size when you show/hide the combo-box.
Regarding your question (You will not need it but just to know in the future):
how do I access the spacer from code
You can create a spacer from code like this:
QSpacerItem* spacer = new QSpacerItem(0, 15, QSizePolicy::Fixed, QSizePolicy::Fixed);
layout->addItem(spacer);
...
Also you can get it from a layout if you know its index:
QLayoutItem* item = layout->itemAt(index);
But there is no such method as show/hide for layout items.

Getting rid of unnecessary scrollbar in QScrollArea

I'm working on a Configuration QDialog. It has several categories (General, Appearance, etc.) that get loaded when the user clicks on them. Each category has its own page. These pages are separate classes in their own right (each has its own ui, cpp, and h). When the Preferences window loads, the pages get instantiated and are loaded into a QStackedWidget. The Stackedwidget is then placed into a QScrollArea so it can scroll if necessary.
However, there is a problem. Ever since I added the QStackedWidget, the QScrollArea always has a vertical scrollbar even when the current page is short enough not to need one: (picture shows the shortest page)
The ScrollArea vertical scroll policy is set to Qt::ScrollBarAsNeeded so logically it should only show a bar if the page is larger than the viewable area.
Here's what I already tried to fix this:
Setting the scroll policy to Qt::ScrollBarAlwaysOff. While this gets rid of the scrollbar, its unacceptable since it doesn't let the user know they need to scroll on long pages.
Setting the Minimum/Maximum heights for the QStackedwidget. This makes the scrollbar go away if I set it for a low enough value, but it is unacceptable since it causes some of the widgets to have a squished appearance.
I know the problem has something to do with the QStackedWidget but since this is the first time I've used QStackedWidget I’m not sure what it is. I've also noticed the scroll is always for the same amount; i.e. the scrollable area is always the same size no matter how large/small the page widget is. For some reason, it's slightly larger than the longest page. At first I thought the vertical spacers I put at at the bottom of each page to tighten up the layout were causing this, but taking them out didn't fix it.
Update: Here's the code that controls the Stackedwidget:
void Newconfig::on_Categories_currentItemChanged(QTreeWidgetItem *current)
{
QModelIndex index=ui->Categories->currentIndex();
int idx=index.row();
QString category=current->text(0);
this->setWindowTitle("Preferences -- " + category);
if (stack->currentWidget() != 0) {
stack->currentWidget()->setSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Ignored);
}
stack->setCurrentIndex(idx);
stack->currentWidget()->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
adjustSize();
}
The QStackedWidget takes the size of the largest widget added to it. In your case, the largest page in your preferences dialog is what is influencing the size of the QStackedWidget and is thus forcing the scroll area to show its scroll bar, even when it doesn't appear to be necessary.
To get around this, you can create a slot that is triggered right before you change the current widget in the QStackedWidget that sets the size policy of the page you are leaving to QSizePolicy::Ignored and the size policy of the page you are about to show to whatever you desire for that page -- QSizePolicy::Expanding for instance. An example of this technique is detailed on this page. There's a lot of code in the example, but the important part is here:
void changeCurrent(int idx)
{
if (stackWidget->currentWidget() !=0) {
stackWidget->currentWidget()->setSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Ignored);
}
stackWidget->setCurrentIndex(idx);
stackWidget->currentWidget()->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
adjustSize();
}
This function is called to change the current index on the QStackedWidget. It causes the page that was just being viewed to have a size policy that has no influence on the overall size of the QStackedWidget and the page that is about to be viewed to have a size policy of consequence.

Making a Flex DataGrid scroll smoothly

I've noticed that the default behaviour for a DataGrid's vertical scroll bar is to scroll one row at a time. This is all well and good when the rows are all uniform and small (e.g. displaying a single line of text), but gets really ugly as soon as you have rows with variable heights.
I'm curious, is there a way to make DataGrid scrolling "smooth"? For instance, is there a way to have the DataGrid scroll by a set number of pixels, lines of text, etc. rather than scrolling one row at a time?
So far, the only solution I've managed to come up with is to place the DataGrid in a Canvas and have the Canvas do the scrolling instead of the DataGrid. The issue with this approach, though, is that as soon as the Canvas scrolls far enough, the DataGrid headers scroll off-screen. Ideally, I'd like to get the smooth-scrolling nature of the Canvas, but also keep the DataGrid headers visible. Is that possible?
The way that ItemRenderer's work in Flex 3 makes smooth scrolling difficult to achieve. Basically Flex recycles item renderers scrolled off of the top of the list as the display objects used for new data at the bottom of the list. Adobe's implementation of most list components in Flex 3 creates and adds these items as they come on to the screen rather than just off the screen, so they "pop in" and smooth scrolling isn't available. I'm not sure why they couldn't have done it in a similar manner for items +/- one position above or below the current scroll pane, but they didn't, and we're stuck with sticky scrolling by default.
Work-arounds do exist, though the one you've noted (dropping the datagrid into a canvas) negates the display-object saving intention of item renderers and incurs a performance cost. This will be fixed for most list-based Flex components in Flex 4, though it won't be fixed immediately for DataGrid. The DataGrid / AdvancedDataGrid component is maintained by a separate team based in India, last time I heard, and so it tends to be a bit behind the rest of the SDK.
I'd recommend trying something similar to this implementation of a smooth-scrolling list by Alex Harui. I'm not sure exactly how well it'd work for DataGrid or AdvancedDataGrid, but this is the most intuitive technique I can think of for making the list scroll correctly.
Try this... It's still based on Alex's code that was mentioned above. His should still be a great start for removing the snap-to-row behavior. Original source:
http://blogs.adobe.com/aharui/2008/03/smooth_scrolling_list.html
Alex's original some code for smooth vertical scrolling but that was not an issue I had with the DataGrid. It was smooth scrolling horizontally that I needed. I am using the DataGrid in an unorthodox manner for analyzing plain text reports output by our database (great way of providing visual feedback on a document). The code below allows content to go off screen and the user can scroll without that snap-to-column behavior.
You can adapt this to use the same math routines for vertical scrolling and then it will make scrolling possible and ignore the snap to row behavior. In particular switch the usage of the listContent.move method to move the contents vertically and use a inverse of the rounded pixel value you calculate from the vertical scroll bar (as opposed to my using the horizontal).
This method is bit simpler than Alex's method from the link above - a lot less code so try adapting and see how it works.
override protected function scrollHandler(event:Event):void
{
// Override the default scroll behavior to provide smooth horizontal scrolling and not the usual "snap-to-column" behavior
var scrEvt:ScrollEvent = event as ScrollEvent;
if(scrEvt.direction == ScrollEventDirection.HORIZONTAL) {
// Get individual components of a scroll bar for measuring and get a horizontal position to use
var scrDownArrow:DisplayObject = horizontalScrollBar.getChildAt(3);
var sctThumb:DisplayObject = horizontalScrollBar.getChildAt(2);
// I replaced maxHorizontalScrollPosition in Alex's code with "1300" to fix my exact application. In other situations you may finding using some property or different value is more appropriate. Don't rely on my choice.
var hPos:Number = Math.round((sctThumb.y - scrDownArrow.height) / (scrDownArrow.y - sctThumb.height - scrDownArrow.height) * 1300);
// Inverse the position to scroll the content to the left for large reports
listContent.move(hPos * -1, listContent.y);
}
// Go ahead and use the default handler for vertical scrolling
else {
super.scrollHandler(event);
}
}

Horizontal scrollbar hides content of ApplicationControlBar

I have an application control bar at the bottom of my Flex application (with attributes width="100%", dock="false", left="0", bottom="0", height="50"). It contains a lot of elements (like buttons and labels). The width of the SWF is the width of the browser.
When the user makes the width of the browser window smaller, after a certain point, the components on the application control bar gets "squished": they are forced on top of each other. And so, it becomes confusing and ugly. To ensure that that doesn't happen, I've set the minWidth attribute to a value so that it'll always display the components without them overlapping each other.
But then, a horizontal scrollbar appears and hides the bottom half of the application control bar.
I've googled and I found this article: flex verticalscrollpolicy bug (referenced by this SO question: Flex: Prevent scrollbar from covering content when automatically displayed).
But that seems to apply only to a fixed size component. My component's width is a percentage. Any ideas on how to have the horizontal scrollbar appear and have it not cover up the application control bar?
Thanks!
See if adding the following code to the overriden validateSize method (as in the scrollpolicy bug page you linked to) solves the problem.
if (width < measuredWidth)
{
height = normal-height + height-of-the-horizontal-scrollbar;
}
else
height = normal-height;
(Find the normal height of the application control bar and the scroll bar (trace them out) and use those values).
So this happens when the ApplicationControlBar is fixed at the bottom: bottom=0 and left=0. The easiest solution is to make the bar a lot taller (that'll push the content way higher than the scrollbar height). But that makes it kinda ugly.
So another solution: in the MXML file, I capture the Resize event. And in that function, I do this:
if (width < bar.minWidth) // width is the width of the SWF
{
bar.height = ORIGINAL_SIZE + 10;
hbox.setStyle("verticalAlign", "top");
hbox.setStyle("verticalCenter", -10);
} else {
// normal case
box.height = ORIGINAL_SIZE;
hbox.setStyle("verticalAlign", "middle");
hbox.setStyle("verticalCenter", 0);
}
And the horizontal scrollbar doesn't hide the content anymore! Also, the Resize event doesn't get triggered when the bar has a minWidth & the width of the stage is less than that.
I had this come up today and I slightly tweaked sri's if statement like this:
if (buttonContainer.horizontalScrollbar)
{
// Change height & style properties
}
else
{
// Return to original properties.
}

Resources