Having a dynamic number of standalone views divide the space evenly - eclipse-rcp

When showing multiple instances of the same view, I would like the following to hold true:
The views do not stack
The number of them is dynamic (let's say 1 to 10) and not known initially: they are added by the user, one by one
They are positioned above/below each other
They take up all the vertical space amongst themselves
By default they have equal height (if there are 3 views, each gets 1/3 of the total vertical space, adding a 4th view causes them all to get 1/4 etc)
What I use to add the views in Perspective is the following:
public void createInitialLayout(IPageLayout layout) {
String editorArea = layout.getEditorArea();
layout.addStandaloneViewPlaceholder(MarkerView.VIEW_ID + ":0", IPageLayout.TOP, 0.6F, editorArea, true);
layout.addStandaloneViewPlaceholder(MarkerView.VIEW_ID + ":1", IPageLayout.BOTTOM, 0.6F, editorArea, true);
layout.addStandaloneViewPlaceholder(MarkerView.VIEW_ID + ":2", IPageLayout.BOTTOM, 0.6F, editorArea, true);
The problem is that not knowing what the number of views will be, the ratios won't provide equal space to all instances of the view.
Is it somehow possible to adjust the ratios of existing views after adding a new one?
I've also tried having the view class implement ISizeProvider (which I admit I do not have a full grasp of), but haven't managed to achieve what I want with that either:
#Override
public int getSizeFlags(boolean width) {
return width ? 0 : SWT.FILL | SWT.MIN | SWT.MAX;
}
#Override
public int computePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredResult) {
int windowHeight = PlatformUI.getWorkbench().getDisplay().getActiveShell().getBounds().height;
int nrOfViews = Model.getInstance().markerCounter;
return width ? preferredResult : windowHeight / nrOfViews;
}
Is this approach something in the right direction at least?

Related

Adapting row height in JFace TableViewer as I scroll my table

I have a JFace TableViewer with an ILazyContentProvider and a StyledCellLabelProvider for each column, which I mostly grabbed from https://wiki.eclipse.org/JFaceSnippets#Snippet006TableMultiLineCells to enable multiline rows. When I open the table, all rows have the height of the row which takes up the most space, as intended. As I scroll down the table, the row heights will change as intended if a row takes up more space. However, this does not currently work in the other direction, i.e., if I scroll so that the current rows showing take up less space, all rows still have the height of the largest row in the whole table.
Is there a way to solve this? Somehow there seems to be a memory of the content that the lazy content provider should be forgetting?
This is my measure method in the StyledCellLabelProvider:
#Override
protected void measure(Event event, Object element) {
event.width = viewer.getTable().getColumn(event.index).getWidth();
if (event.width == 0) {
return;
}
TableEntryData rowData = (TableEntryData) element;
TableCellData cellData = getCellData(rowData, event.index);
int height = event.gc.textExtent(SOME_STRING).y; // Height of a written string on one line.
int lines = cellData.getPoints().size();
event.height = height * lines;
event.gc.dispose();
}
and this is most of my ILazyContentProvider:
#Override
public void updateElement(int index) {
viewer.replace(entries.get(index), index);
}
#SuppressWarnings("unchecked") // TODO:
#Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.entries = (List<TableEntryData>) newInput;
}
The measure item code in the Table class (used by TableViewer) will never reduce the size of rows once it has grown bigger, so there is no way to change this.
The measure code is Table.sendMeasureItem but it can't be overridden. The code here is platform specific, but I checked both the Windows and macOS version (not sure about the Linux/GTK version).
I do have a hack to work around this, but it is platform dependent and I only have it for macOS.

Hw to Freeze the Button in Scroll bar

I am using unity 2018.3.7. In my project I have instantiate 25 buttons in the scroll bar..I am scrolling the button. It is working well.
Actually i want freeze the 12 the button.When i scroll the button. The 12 th button should be constantly should be there and other button should scroll go up.
scrolling should be done but the freeze button should be constantly there.
Like Excel. If we freeze the top row. The top row is constantly there and scrolling is happened.
Like that i have to do in unity.
How to Freeze the button in scroll bar.
Edit:
Actually I have uploaded new gif file. In that gif file 2 row is freezed (Row Heading1,Row Heading2, Row Heading3,RowHeading4).
2nd row is constantly there. Rest of the the rows 4 to 100 rows are going up.
Like that i have to do ...
How can i do it..
Though your question still is very broad I guess I got now what you want. This will probably not be exactly the solution you want since your question is quite vague but it should give you a good idea and starting point for implementing it in the way you need it.
I can just assume Unity UI here is the setup I would use. Since it is quite complex I hope this image will help to understand
so what do we have here:
Canvas
has the RowController script attached. Here reference the row prefab and adjust how many rows shall be added
Panel is the only child of Canvas. I just used it as a wrapper for having a custom padding etc - it's optional
FixedRowPanel
Here we will add the fixed rows on runtime
Initially has a height of 0!
Uses anchore pivot Y = 1! This is later important for changing the height on runtime
Uses a Vertical Layout Group for automatically arranging added children
ScrollView - Your scrollview as you had it but
Uses streched layout to fill the entire Panel (except later the reduced space for the fixed rows)
Uses anchor pivot Y = 1! Again important for changing the height and position on runtime later
Viewport afaik it should already use streched anchors by default but not sure so make it
Content
Uses a Vertical Layout Group
Initially has a height of 0 (but I set this in the code anyway) and will grow and shrink accordingly when adding and removing rows
And finally RowPrefab
I didn't add its hierachy in detail but it should be clear. It has a Toggle and a Text as childs ;)
Has the Row script attached we use for storing and getting some infos
Now to the scripts - I tried to comment everything
The Row.cs is quite simple
public class Row : MonoBehaviour
{
// Reference these via the Inspector
public Toggle FixToggle;
public Text FixTogText;
public Text RowText;
public RectTransform RectTransform;
// Will be set by RowController when instantiating
public int RowIndex;
private void Awake()
{
if (!RectTransform) RectTransform = GetComponent<RectTransform>();
if (!FixToggle) FixToggle = GetComponentInChildren<Toggle>(true);
if (!FixTogText) FixTogText = FixToggle.GetComponentInChildren<Text>(true);
}
}
And here is the RowController.cs
public class RowController : MonoBehaviour
{
public Row RowPrefab;
public RectTransform ScrollView;
public RectTransform Content;
public RectTransform FixedRowParent;
public int HowManyRows = 24;
public List<Row> CurrentlyFixedRows = new List<Row>();
public List<Row> CurrentlyScrolledRows = new List<Row>();
// Start is called before the first frame update
private void Start()
{
// initially the content has height 0 since it has no children yet
Content.sizeDelta = new Vector2(Content.sizeDelta.x, 0);
for (var i = 0; i < HowManyRows; i++)
{
// Create new row instances and set their values
var row = Instantiate(RowPrefab, Content);
// store the according row index so we can later sort them on it
row.RowIndex = i;
row.RowText.text = $"Row Number {i + 1}";
// add a callback for the Toggle
row.FixToggle.onValueChanged.AddListener(s => HandleToggleChanged(row, s));
// increase the content's size to fit the children
// if you are using any offset/padding between them
// you will have to add it here as well
Content.sizeDelta += Vector2.up * row.RectTransform.rect.height;
// don't forget to add them to this list so we can easily access them
CurrentlyScrolledRows.Add(row);
}
}
// called every time a row is fixed or unfixed via the Toggle
private void HandleToggleChanged(Row row, bool newState)
{
if (newState)
{
// SET FIXED
// Update the button text
row.FixTogText.text = "Unfix";
// Move this row to the fixedRow panel
row.transform.SetParent(FixedRowParent);
// be default we assume we want the first position
var targetIndex = 0;
// if there are other fixed rows already find the first child of FixedRowParent that has a bigger value
if (CurrentlyFixedRows.Count > 0) targetIndex = CurrentlyFixedRows.FindIndex(r => r.RowIndex > row.RowIndex);
// handle case when no elements are found -> -1
// this means this row is the biggest and should be the last item
if (targetIndex < 0) targetIndex = CurrentlyFixedRows.Count;
// and finally in the hierachy move it to that position
row.transform.SetSiblingIndex(targetIndex);
// insert it to the fixed list and remove it from the scrolled list
CurrentlyFixedRows.Insert(targetIndex, row);
CurrentlyScrolledRows.Remove(row);
// Make the fixed Panel bigger about the height of one row
FixedRowParent.sizeDelta += Vector2.up * row.RectTransform.rect.height;
// Make both the scrollView and Content smaller about one row
Content.sizeDelta -= Vector2.up * row.RectTransform.rect.height;
ScrollView.sizeDelta -= Vector2.up * row.RectTransform.rect.height;
// Move the scrollView down about one row in order to make space for the fixed panel
ScrollView.anchoredPosition -= Vector2.up * row.RectTransform.rect.height;
}
else
{
// SET UNFIXED - Basically the same but the other way round
// Update the button text
row.FixTogText.text = "Set Fixed";
// Move this row back to the scrolled Content
row.transform.SetParent(Content);
// be default we assume we want the first position
var targetIndex = 0;
// if there are other scrolled rows already find the first child of Content that has a bigger value
if (CurrentlyScrolledRows.Count > 0) targetIndex = CurrentlyScrolledRows.FindIndex(r => r.RowIndex > row.RowIndex);
// handle case when no elements are found -> -1
// this means this row is the biggest and should be the last item
if (targetIndex < 0) targetIndex = CurrentlyScrolledRows.Count;
// and finally in the hierachy move it to that position
row.transform.SetSiblingIndex(targetIndex);
// insert it to the scrolled list
CurrentlyScrolledRows.Insert(targetIndex, row);
// and remove it from the fixed List
CurrentlyFixedRows.Remove(row);
// shrink the fixed Panel about ne row height
FixedRowParent.sizeDelta -= Vector2.up * row.RectTransform.rect.height;
// Increase both Content and Scrollview height by one row
Content.sizeDelta += Vector2.up * row.RectTransform.rect.height;
ScrollView.sizeDelta += Vector2.up * row.RectTransform.rect.height;
// Move scrollView up about one row height to fill the empty space
ScrollView.anchoredPosition += Vector2.up * row.RectTransform.rect.height;
}
}
}
Result:
As you can see I can now fix and unfix rows dynamically while keeping their correct order within both according panels.

Growing menu with scrollbar

I hate to ask such a generic question but I'm really stuck and super hoping someone can help me along the way. Here is the situation:
I'm making the GUI for a mobile app, portrait mode. I'm using canvas scalers to scale my canvasses with a reference width of 1080. This means I effectively don't know the height of my screen space.
What I want to create is a menu with a variable amount of items. The menu must be anchored to the bottom (with an offset margin) and grow upwards. So far I've been able to manage this using VerticalLayoutGroup and anchoring the rect transform to the bottom.
But my last requirement is that if the content would grow too big, a scrollbar would appear. The definition of the content being too big is: if it would extend the (unknown) screen height ( minus the offset margin of course). I hope the following image illustrates this much clearer:
I have a unity project here: https://ufile.io/v31br
Did you give a try to scrollView? here it is: https://unity3d.com/learn/tutorials/topics/user-interface-ui/scroll-view
You can use your vertical layout inside it and you will probably want to deactivate horizontal scroll and delete the horizontal slider.
Via script you can check its rectTransform height and compare it to your container's size, when reached maxHeight you can start managing your item's sizes
I assume you use the ScrollRect component as it is the right component to use in your case.
You can check the screen height with the Screen.height property.
Once you know the screen height you can compare it with your rect height and toggle the scrollbar with the ScrollRect.vertical property. You may have to change the ScrollRect.verticalScrollbarVisibility to permanent in order to make it work for you.
The answer Dave posted was close, but the problem is that the scrollview doesn't expand. I fixed it eventually by stretching the scrollview and resizing the parent manually as items are added. I set the anchors to the maximum size and adjust the sizeDelta.
public class MenuScript : MonoBehaviour
{
public int MenuItemCount;
public GameObject MenuItemPrefab;
public Transform MenuItemParent;
private RectTransform _rectTransform;
void Start()
{
_rectTransform = GetComponent<RectTransform>();
for (var i = 0; i <= MenuItemCount; i++)
{
GameObject instance = Instantiate(MenuItemPrefab, MenuItemParent, false);
instance.GetComponent<Text>().text = instance.name = "Item " + i;
float size = instance.transform.GetComponent<RectTransform>().sizeDelta.y;
TryExpandBy(size + 10);
}
}
private void TryExpandBy(float size)
{
var deltaY = _rectTransform.sizeDelta.y + size;
if (deltaY > 0) deltaY = 0;
_rectTransform.sizeDelta = new Vector2(_rectTransform.sizeDelta.x, deltaY);
}
}

LIBGDX - Image Button - join images

I am trying to use de Image Button on LIBGDX to create a button based on two images.
Using add to second image, works fine, but have one problem.
The images are of different sizes.
Note: I am testing with the same picture to see the result
Is there a way to correct this? Using some scale to the images?
levelsTexture = new Texture(Gdx.files.internal("level1.png"));
levels = new TextureRegion(levelsTexture).split(TILE_WIDTH, TILE_HEIGHT);
ImageButton levels_image = new ImageButton(new TextureRegionDrawable(new
TextureRegion(levels[0][0])));
levels_image.add(new Image (levels[0][0]));
stage.addActor(levels_image);
levels_image.setScale(2f);
The problem:
ImageButton is an extension of the Table class and typically the ImageButton images are set as the background. Using the "add" method for the second image like you did might work kind of, but it behaves differently than setting the background and it also might not be what you want if you want the the second image to also change when you click the button.
The easiest way to add two images to a single ImageButton would be to simply combine the two images in Photoshop (or equivalent) and use that single image on the ImageButton.
The more advanced (and more flexible) method would be to combine the two images programmatically and use this as the background for your ImageButton. This can be done by creating a custom class which extends BaseDrawable and have it take two Images in the constructor. If you want your images stacked on top of each other, set the minHeight of your custom drawable class to be the combined height of your two images. Then override the draw method and draw your two images on top of each other like this:
#Override
public void draw(Batch batch, float x, float y, float width, float height){
img1.getDrawable().draw(batch, x, y, img1.getWidth(), img1.getHeight());
img2.getDrawable().draw(batch, x, y+img1.getHeight(), img2.getWidth(), img2.getHeight());
}
}
The ImageButton takes a Drawable in its constructor, so you can pass this object right into the button when you create it and both of your Images should appear in the button and they will be treated as one.
I've done something similar to make a background for a table using multiple Images and this method works great.
I wrote my own SpriteButton class in which I implement this method for scaling my textures.
private Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
int original_width = imgSize.width;
int original_height = imgSize.height;
int bound_width = boundary.width;
int bound_height = boundary.height;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
//scale width to fit
new_width = bound_width;
//scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
//scale height to fit instead
new_height = bound_height;
//scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
return new Dimension(new_width, new_height);
}
Then finally you draw the texture with the output dimension as as size to draw.
sb.begin();
sb.draw(this.texture2, this.x, this.y, dim_size_new.width, dim_size_new.height);
sb.end();
So you could technically draw the second image to the size of the the first image, if the first image size is correct.
Your implementation will look different from mine but you should be able to figure it out form here on out.

Resizeable quadratic grid

I would like to display a quadratic GridPane inside a window. The window can have every possible size and normally width and height are not equal. Anyway I would like to display the GridPane as square centered like this:
respectively
Ideally there is a configurable padding around the square. The pictures are taken from a canvas approach, but I want to switch to standard controls. Can anyone give me some hints how to achieve this?
One way to achieve an squared layout for your grid for any resolution of your window is rescaling it to use the maximum size of the window (after some padding), while keeping the squared size.
Instead of a GridPane, a simple Group is more flexible for moving its children, though it requires manual layouting of them.
This simple snippet is based on the JavaFX implementation of the 2048 game you can find here. It uses styleable rectangles to create a grid, and over them the 'tiles' are added. To find out more about styling, layouting or tile movement, go to the refered link.
private static final int NUM_CELLS = 15;
private static final int CELL_SIZE = 50;
private static final int TILE_SIZE = CELL_SIZE-15;
private final static int MARGIN = 20;
#Override
public void start(Stage primaryStage) {
// create background square grid
Group gridGroup = new Group();
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
Rectangle cell = new Rectangle(i * CELL_SIZE, j * CELL_SIZE, CELL_SIZE, CELL_SIZE);
cell.setFill(Color.WHEAT);
cell.setStroke(Color.GREY);
gridGroup.getChildren().add(cell);
})
);
// Add grid to board
Group board = new Group(gridGroup);
// add random tiles to board
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
if(i==j || NUM_CELLS-i-1==j){
Label label = new Label();
label.setMinSize(TILE_SIZE, TILE_SIZE);
label.setPrefSize(TILE_SIZE, TILE_SIZE);
label.setMaxSize(TILE_SIZE, TILE_SIZE);
label.setStyle("-fx-background-color: red; -fx-background-radius: 50");
label.setLayoutX((i+0.5)*CELL_SIZE-TILE_SIZE/2);
label.setLayoutY((j+0.5)*CELL_SIZE-TILE_SIZE/2);
board.getChildren().add(label);
}
})
);
Bounds gameBounds = board.getLayoutBounds();
StackPane root = new StackPane(board);
// Listener to rescale and center the board
ChangeListener<Number> resize = (ov, v, v1) -> {
double scale = Math.min((root.getWidth() - MARGIN) / gameBounds.getWidth(),
(root.getHeight() - MARGIN) / gameBounds.getHeight());
board.setScaleX(scale);
board.setScaleY(scale);
board.setLayoutX((root.getWidth() - gameBounds.getWidth()) / 2d);
board.setLayoutY((root.getHeight() - gameBounds.getHeight()) / 2d);
};
root.widthProperty().addListener(resize);
root.heightProperty().addListener(resize);
Scene scene = new Scene(root);
// Maximum size of window
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double factor = Math.min(visualBounds.getWidth() / (gameBounds.getWidth() + MARGIN),
visualBounds.getHeight() / (gameBounds.getHeight() + MARGIN));
primaryStage.setScene(scene);
primaryStage.setWidth((gameBounds.getWidth() + MARGIN) * factor);
primaryStage.setHeight((gameBounds.getHeight() + MARGIN) * factor);
primaryStage.show();
}
And this is how it will look like after some window resizing: