automatically arranging gwt buttons depending on size - java

I'm new to GWT and therefore need help with the following:
I am working with UIBinder and need to arrange three gwt buttons with dynamic labels in a special way (see picture attached). How do I do this? I'm grateful for any help!

As I said in my comment you just arrange your widgets with an additional CSS style to float them left. As a GWT container, there is FlowPanel.
CSS:
.yourWidget {
float: left;
..other styles..
}
.container {
width: 300px;
overflow: hidden;
}
Code:
YourButtonWidget w1 = new YourButtonWidget("Some long label");
w1.addStyleNames("yourWidget"):
YourButtonWidget w2 = new YourButtonWidget("Label");
w2.addStyleNames("yourWidget"):
YourButtonWidget w3 = new YourButtonWidget("Another Label");
w3.addStyleNames("yourWidget"):
FlowPanel container = new FlowPanel();
container.add(w1);
container.add(w2);
container.add(w3);
Obviously you can integrate the CSS style as part of your widget representing your button. Just remember that your widget must assign a width to itself or float: left; obviously won't work.
UPDATE: I just read your last comment, for that you would have to just develop an algorithm to adjust the button 1 and 2 width to fill all available space in the container. float: left; will stil work, you just have to work out the size of the buttons.
With 3 buttons it's easy (where button1Width and button2Width are dynamic and button3Width is fixed), here is how to work out the final widths (continuing from above):
int containerWidth = container.getElement().getOffsetWidth();
int button1Width = w1.getElement().getOffsetWidth();
int button2Width = w2.getElement().getOffsetWidth();
int button3Width = w3.getElement().getOffsetWidth();
if ( (button1Width + button2Width) > containerWidth ) {
button1Width = containerWidth;
if (( button2Width + button3Width ) > containerWidth) {
button2Width = containerWidth;
} else {
button2Width = containerWidth - button3Width;
}
} else {
button2Width = containerWidth - button1Width;
}
w1.getElement().getStyle().setWidth(button1Width,Unit.PX);
w2.getElement().getStyle().setWidth(button2Width,Unit.PX);
w3.getElement().getStyle().setWidth(button3Width,Unit.PX);
Just make sure that you do these adjustments after you attach everything to the DOM, otherwise the widths will not be available obviously.

Related

Android: custom bottom navigation view with one center big icon [duplicate]

How to create BottomNavigation with one of the item is larger than the parent, but without using floatingActionButton. For example like this:
I tried to do that by wrapping the icon with Box but it get cut like this:
Then i try to separate that one button and use constraintLayout to position it, but the constraintLayout cover the screen like this. Even when i color it using Color.Transparent, it always feels like Color.White (i dont know why Color.Transparent never work for me). In this picture i give it Red color for clarity reason.
So how to do this kind of bottomNavBar without having to create heavy-custom-composable?
Update: so i try to make the code based on MARSK and Dharman comment (thanks btw). This is what i
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(Color.Transparent)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.White)
.align(Alignment.BottomCenter)
)
Row(
modifier = Modifier
.zIndex(56.dp.value)
.fillMaxWidth()
.selectableGroup(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
items.forEach { item ->
val selected = item == currentSection
BottomNavigationItem(
modifier = Modifier
.align(Alignment.Bottom)
.then(
Modifier.height(
if (item == HomeSection.SCAN) 84.dp else 56.dp
)
),
selected = selected,
icon = {
if (item == HomeSection.SCAN) {
ScanButton(navController = navController, visible = true)
} else {
ImageBottomBar(
icon = if (selected) item.iconOnSelected else item.icon,
description = stringResource(id = item.title)
)
}
},
label = {
Text(
text = stringResource(item.title),
color = if (selected) Color(0xFF361DC0) else LocalContentColor.current.copy(
alpha = LocalContentAlpha.current
),
style = TextStyle(
fontFamily = RavierFont,
fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 18.sp,
),
maxLines = 1,
)
},
onClick = {
if (item.route != currentRoute && item != HomeSection.SCAN) {
navController.navigate(item.route) {
launchSingleTop = true
restoreState = true
popUpTo(findStartDestination(navController.graph).id) {
saveState = true
}
}
}
}
)
}
}
}
It works in preview, but doesn't work when i try in app.
This one in the preview, the transparent working as expected:
And this is when i try to launch it, the transparent doesnt work:
Note: I assign that to bottomBar of Scaffold so i could access the navigation component. Is it the cause that Transparent Color doesnt work?
Update 2: so the inner paddingValues that makes the transparent doesnt work. I fixed it by set the padding bottom manually:
PaddingValues(
start = paddingValues.calculateStartPadding(
layoutDirection = LayoutDirection.Ltr
),
end = paddingValues.calculateEndPadding(
layoutDirection = LayoutDirection.Ltr
),
top = paddingValues.calculateTopPadding(),
bottom = SPACE_X7,
)
Custom Composable are not heavy, really.
Anyway, try this:-
Create a Container of MaxWidth (maybe a BoxWithConstraints or something), keep its background transparent, set the height to wrap content. Create the tabs as usual, but keeping the bigger tab's icon size bigger explicitly using Modifier.size(Bigger Size).
After you have this setup, add another container inside this container with white background, covering a specific height of the original container. Let's say 60%
Now set the z-index of all the icons and tabs to higher than the z-index of this lastly added container. Use Modifier.zIndex for this. And viola, you have your Composable ready.
In order to set a specific percentage height of the inner container, you will need access to the height of the original container. Use BoxWithConstraints for that, or just implement a simple custom Layout Composable

SWT Table column offset/padding

I have an SWT Table with multiple columns. In this example, let's say there are three columns. The cells of each column all contain an image followed by some text (example pictured below). However, as you can see there is no spacing between the 2 - 3 column lines and their cells.
Is there a built-in way to add a buffer zone to those cells so the icons don't appear right on the edge line? Such as an offset property of some kind? I don't see any property overtly listed in either Table or TableColumn.
If not, is there a workaround beyond just adding white space to the cell images?
Please let me know if there's anything I can do to make my question clearer.
Table:
I don't think there is a designated way to adjust the margin and spacing of image and text within a cell. Apart from adding transparent pixels to the image (as you already suggested), you can use a PaintListener to gain control over how a cell is rendered.
The example below draws image and text with adjustable margin and spacing:
Listener paintListener = new Listener() {
int leftMargin = 40;
int rightMargin = 10;
int imageSpacing = 200;
#Override
public void handleEvent( Event event ) {
TableItem item = ( TableItem )event.item;
Rectangle imageBounds = image.getBounds();
Point textExtent = event.gc.textExtent( item.getText() );
switch( event.type ) {
case SWT.MeasureItem: {
event.width += leftMargin + imageBounds.width + imageSpacing + textExtent.x + rightMargin;
event.height = Math.max( event.height, imageBounds.height + 2 );
event.height = Math.max( event.height, textExtent.y + 2 );
break;
}
case SWT.PaintItem: {
int x = event.x + leftMargin;
int imageOffset = ( event.height - imageBounds.height ) / 2;
event.gc.drawImage( image, x, event.y + imageOffset );
x += imageSpacing;
int textOffset = ( event.height - textExtent.y ) / 2;
event.gc.drawText( item.getText(), x, event.y + textOffset );
break;
}
case SWT.EraseItem: {
event.detail &= ~SWT.FOREGROUND;
}
}
}
};
table.addListener( SWT.MeasureItem, paintListener );
table.addListener( SWT.PaintItem, paintListener );
table.addListener( SWT.EraseItem, paintListener );
For a more thorough understanding of owner-drawn items in SWT please read the Custom Drawing Table and Tree Items article.
If you are using a JFace TableViewer, there is also an OwnerDrawLabelProvider as shown in this example:
http://www.vogella.com/tutorials/EclipseJFaceTableAdvanced/article.html#styledcelllabelprovider-and-ownerdrawlabelprovider

How to position objects on JPanel in a circular fashion without using NullLayout?

So my problem is this: I have a class extending JProgressBar and basically it looks like a flashing circle(it also breaks into segments depending on the amount of tasks, assigned to that indicator and I need to use setUI each time I switch indicator to the next task, which is pretty bad, but it'll do for now). I need to position 37 of those circles on a JPanel so that they form a circle of their own. Right now I do it like this:
private void addToPane(JPanel pane){
pane.setLayout(null);
Insets insets = pane.getInsets();
int width = pane.getWidth();
int height = pane.getHeight();
Dimension size = new Dimension(30, 30);
//Dimension mid = new Dimension(width/2, height/2);
Dimension mid = new Dimension(200, 250);
int r = 210;
double revox, revoy, angle = -Math.PI/2;
double revangle = 2*Math.PI/37;
for(int i=1; i<38; i++){
FlashingIndicator templabel = new FlashingIndicator();
templabel.setPreferredSize(size);
templabel.setUI(new ProgressIndicator(flash, 0, false));
templabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
indicate.add(templabel);
revox = r*Math.cos(angle);
revoy = r*Math.sin(angle);
indicate.get(i - 1).setBounds(insets.left + mid.width + (int) revox, insets.top + mid.height + (int) revoy, size.width, size.height);
pane.add(indicate.get(i - 1));
angle-=revangle;
}
}
No need to say: this is pretty bad. I wanted to locate them depending on the size of the panel, but when the function is being called in createUIComponents() (I use IntelliJ Idea's GUI builder) - the panel is not properly created yet, so getWidth() just returns 0. Using random numbers like 200 and 250 is bad for the obvious reasons. Also seems like the general consensus is: Null Layout is bad and I shouldn't use it. So here's the question:
Which LayoutManager should I use in order to locate indicators properly? All I can think of is GridLayout, but the way I do it now indicators nicely overlap a bit, using a grid will make it look rough. And if I can't use managers for this - how can I make it dependnant on the size of the panel?
Right now it looks like this:
Override the paintDeterminate() method in a custom BasicProgressBarUI to render your indicator; a related example is seen here. You can scale the rendering, as shown here, in a way that fills the component; this will obviate the need for a custom layout manager internal to your component. Override getPreferredSize(), as discussed here, so that your custom progress indicator works correctly with enclosing layouts.

Android's LinearLayout equivalent in Swing

I have seen this thread which asked the exact same question I have now, but find the answers a bit unsatisfactory:
Android's LinearLayout for Swing
I created a class WeightedPanel like so:
public class WeightedPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 6844740568601141924L;
private boolean mVertical;
private double mLastWeight = 1;
private GridBagConstraints mConstraints;
private int mLastGrid = 0;
public WeightedPanel(boolean vertical) {
mVertical = vertical;
mConstraints = new GridBagConstraints();
setLayout(new GridBagLayout());
}
#Override
public Component add(Component comp) {
return add(comp, mLastWeight);
}
public Component add(Component comp, double weight) {
if (mVertical) {
mConstraints.weighty = weight;
mConstraints.weightx = 1;
mConstraints.gridy = mLastGrid;
mConstraints.gridx = 0;
} else {
mConstraints.weightx = weight;
mConstraints.weighty = 1;
mConstraints.gridx = mLastGrid;
mConstraints.gridy = 0;
}
mConstraints.fill = GridBagConstraints.BOTH;
add(comp, mConstraints);
mLastWeight = weight;
mLastGrid += weight;
return comp;
}
public Component add(Component comp, int weight) {
return add(comp, (double) weight);
}
}
This kind of works, but I have two problems with it:
1) In my application, I have a login screen:
#Override
protected void addComponents(WeightedPanel jPanel) {
mUpdateListener = new UpdateListener() {
#Override
public void onUpdate() {
LoginFrame.this.onUpdate();
}
};
WeightedPanel panel = getUserPanel();
jPanel.add(panel);
panel = getPasswordPanel();
jPanel.add(panel);
mLoginButton = getLoginButton();
jPanel.add(mLoginButton);
}
private WeightedPanel getPasswordPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("Password");
result.add(label);
mPasswordField = new PasswordField(mUpdateListener);
result.add(mPasswordField);
return result;
}
private WeightedPanel getUserPanel() {
WeightedPanel result = new WeightedPanel(false);
JLabel label = new JLabel("User");
result.add(label);
mUserTextField = new TextField(mUpdateListener);
result.add(mUserTextField);
return result;
}
which in practice looks like this:
Click to view
Why aren't the labels and text fields all the same size here? I figure it's got something to do with the fact that "Password" is a longer string than "User", but that's obviously not what I want!
2) My second problem is this. I have another screen like so:
#Override
protected void addComponents(WeightedPanel jPanel) {
WeightedPanel scrollPanePanel = getOrdersScrollPane();
jPanel.add(scrollPanePanel);
WeightedPanel buttonPanel = getButtonPanel();
jPanel.add(buttonPanel);
}
private WeightedPanel getOrdersScrollPane() {
WeightedPanel result = new WeightedPanel(true);
JPanel filterPanel = getFilterPanel();
result.add(filterPanel, 1);
mTableModel = new OrdersTableModel();
mTable = new JTable(mTableModel);
mTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
checkEnabled();
}
});
JScrollPane scrollPane = new JScrollPane(mTable);
result.add(scrollPane, 40);
return result;
}
It really doesn't look bad in practice:
Click to view
But have a look at the getOrdersScrollPane() function. The call to functions result.add(filterPanel, 1); and result.add(scrollPane, 50); say that the proportion between the filter panel and the scroll pane should be 1:50, but looking at the scroll pane, it's definitely not 50 times the size of the filter panel. Obviously, I am exaggerating to make my point, I don't really want a proportion of 1:50; it just strikes me that it makes no difference whether I do result.add(scrollPane, 10); or result.add(scrollPane, 50);
Both questions stem from an incorrect understanding of GridBagLayout. A bit more reading and experimenting should help) To answer the question at hand:
1) The problem here is that you want a single GridBagLayout, but instead are adding 2 independent panels.
The result: The columns in the top grid bag are independent of the columns in the bottom grid bag.
To rectify this, there are 2 things you can try:
Add both labels and both text fields to a single GridBag panel. That way the columns will align.
Make a minimum and preferred size for the labels so that their width matches and set their weightx to 0 (and weightx of text fields non-zero). That way you are making the GridBags allocate the same amount of space for the labels and text fields.
The first method is preferred, but not always possible. The second method is hacky and will likely break as soon as you change the label string, a user set a different default font etc, etc.
2) Here you are misunderstanding what weighty does.
It does not make your components of the specified proportion. That should be clear enough since you can mix 0 and non-0 weight components in a single layout.
What it does, is it allocates the preferred (or minimum) sizes for components, and distributes the remaining space in that proportion. Which means if you make your panel 100 pixels higher by resizing the window, 2 will go to the top panel adding spacing, and 98 will go to the table.
What you likely wanted is to make the weighty of the top filter 0 (so that there is no awkward spacings in large windows) and control its actual height with setPreferred and setMinimum size (or by setting those on the embedded components).
EDIT
As docs for Linear Layout state, to achieve a fixed proportion of sizes of components (the initial problem), one has to set their preferred sizes to 0, and then set weights (then all space is remaining space, and is distributed according to weights only). This also works for the GridBag variant.

How to adjust position of scroll in the scrollpane

I have created JTextpane and inserted components inside textpane (components like Jtextarea). (vertical scrollbar of )Jscrollpane of JTextpane is automatically set to bottom when I insert new components in that JTextpane. I want to keep it to be set to the top position. How can I do this
Thanks
Sunil Kumar Sahoo
Here's a utility class I use. It can be used to scroll to the top, bottom, left, right or horizonatal / vertical center of a JScrollPane.
public final class ScrollUtil {
public static final int NONE = 0, TOP = 1, VCENTER = 2, BOTTOM = 4, LEFT = 8, HCENTER = 16, RIGHT = 32;
private static final int OFFSET = 100; // Required for hack (see below).
private ScrollUtil() {
}
/**
* Scroll to specified location. e.g. <tt>scroll(component, BOTTOM);</tt>.
*
* #param c JComponent to scroll.
* #param part Location to scroll to. Should be a bit-wise OR of one or moe of the values:
* NONE, TOP, VCENTER, BOTTOM, LEFT, HCENTER, RIGHT.
*/
public static void scroll(JComponent c, int part) {
scroll(c, part & (LEFT|HCENTER|RIGHT), part & (TOP|VCENTER|BOTTOM));
}
/**
* Scroll to specified location. e.g. <tt>scroll(component, LEFT, BOTTOM);</tt>.
*
* #param c JComponent to scroll.
* #param horizontal Horizontal location. Should take the value: LEFT, HCENTER or RIGHT.
* #param vertical Vertical location. Should take the value: TOP, VCENTER or BOTTOM.
*/
public static void scroll(JComponent c, int horizontal, int vertical) {
Rectangle visible = c.getVisibleRect();
Rectangle bounds = c.getBounds();
switch (vertical) {
case TOP: visible.y = 0; break;
case VCENTER: visible.y = (bounds.height - visible.height) / 2; break;
case BOTTOM: visible.y = bounds.height - visible.height + OFFSET; break;
}
switch (horizontal) {
case LEFT: visible.x = 0; break;
case HCENTER: visible.x = (bounds.width - visible.width) / 2; break;
case RIGHT: visible.x = bounds.width - visible.width + OFFSET; break;
}
// When scrolling to bottom or right of viewport, add an OFFSET value.
// This is because without this certain components (e.g. JTable) would
// not scroll right to the bottom (presumably the bounds calculation
// doesn't take the table header into account. It doesn't matter if
// OFFSET is a huge value (e.g. 10000) - the scrollRectToVisible method
// still works correctly.
c.scrollRectToVisible(visible);
}
}
I have found that the easiest way to do this is the following:
public void scroll(int vertical) {
switch (vertical) {
case SwingConstants.TOP:
getVerticalScrollBar().setValue(0);
break;
case SwingConstants.CENTER:
getVerticalScrollBar().setValue(getVerticalScrollBar().getMaximum());
getVerticalScrollBar().setValue(getVerticalScrollBar().getValue() / 2);
break;
case SwingConstants.BOTTOM:
getVerticalScrollBar().setValue(getVerticalScrollBar().getMaximum());
break;
}
}
I placed this in an object which extended JScrollPane but you could also add the name of your JScrollPane before all the getVertivalScrollBar(). There is two setValue()s for CENTER because getMaximum() returns the bottom of the JScrollBar, not the lowest value it goes to. This also works for Horizontal Scrolling using getHorizontalScrollBar() in place of getverticalScrollBar().
It should be possible to set the DefaultCaret update policy to NEVER_UPDATE. See the article Text Area Scrolling for other uses.
There are various methods that you can use, depending on what is inside the scrollpane. See the tutorial, the very last section.
This works too:
JTextArea. myTextArea;
// ...
myTextArea.select(0, 0); // force the scroll value to the top
jScrollPane.getVerticalScrollBar().setValue(1);
ScrollPane's scroll value is always between ( 0.0 - 1 )
for example
0.0 = 0%
0.1 = 10%
0.2 = 20%
0.25 = 25%
.... so on
And you can adjust the scroll position using these values. For example, in JavaFX
// suppose this is the scrollpane
ScrollPane pane = new ScrollPane();
// to scroll the scrollpane horizontally 10% from its current position
pane.setHvalue(pane.getHvalue() + 0.1);
// to scroll 100%
pane.setHvalue(1);
and so on...
apply logic as you need

Categories