Range Slider with ETO form - forms

A range slider is a slider with two "knobs" and the second "knob" must always have a value > that of the first "knob".
What would be the best way to achieve a range slider in ETO forms?
looks like the Slider : Control class does not expose enough information to create a slider with double Knobs.
Perhaps one way to "fake" it is to put two slider objects side by side, where the value of the first slider becomes the min value of the second slider (this will require some resizing too)?
If we were to create a custom double slider object,
http://pages.picoe.ca/docs/api/html/T_Eto_Forms_Slider.htm

To create custom controls in Eto.Forms, you can use the Drawable class, which gives you a Paint event that you can draw the UI you need, handle mouse events, etc. Setting CanFocus to true allows it to be focused so you can handle key events. For example:
public class RangeSlider : Drawable
{
public RangeSlider()
{
CanFocus = true;
Size = new Size(200, 20);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw the range slider using e.Graphics
}
}

Related

Unity3D How can I select multiple objects in 3D with a drag and select / lasso select?

I am struggling to find a good tutorial or informations that would allow me to select multiple objects in 3D in a user friendly manner.
So far, the best tutorial I found is this one : https://sharpcoderblog.com/blog/unity-3d-rts-style-unit-selection. The tutorial works by using the transform.position of the selectable objects and checking if it within the user's selection.
What I wish is to have the user be able to select a unit even if it is only partially within the user's selection such as most RTS games do ( both in 2D and 3D ).
One possibility would be to create a temporary mesh using the camera's clipping distances and the user's selection and then check for collisions but I was not able to find any tutorials using this method nor do I know if it is the best approach to the subject.
If I understand correctly you want to
somehow start a selection
collect every object that was "hit" during the collection
somehow end the collection
Couldn't you simply use raycasting? I will assume simple mouse input for now but you could basically port this to whatever input you have.
// Just a little helper class for an event in the Inspector you can add listeners to
[SerializeField]
public class SelectionEvent : UnityEvent<HashSet<GameObject>> { }
public class SelectionController : MonoBehaviour
{
// Adjust via the Inspector and select layers that shall be selectable
[SerializeField] private LayerMask includeLayers;
// Add your custom callbacks here either via code or the Inspector
public SelectionEvent OnSelectionChanged;
// Collects the current selection
private HashSet<GameObject> selection = new HashSet<GameObject>();
// Stores the current Coroutine controlling he selection process
private Coroutine selectionRoutine;
// If possible already reference via the Inspector
[SerializeField] private Camera _mainCamera;
// Otherwise get it once on runtime
private void Awake ()
{
if(!_mainCamera) _mainCamera = Camera.main;
}
// Depending on how exactly you want to start and stop the selection
private void Update()
{
if(Input.GetMouseButtonDown(0))
{
StartSelection();
}
if(Input.GetMouseButtonUp(0))
{
EndSelection();
}
}
public void StartSelection()
{
// if there is already a selection running you don't wanr to start another one
if(selectionRoutine != null) return;
selectionRoutine = StartCoroutine (SelectionRoutine ());
}
public void EndSelection()
{
// If there is no selection running then you can't end one
if(selectionRoutine == null) return;
StopCoroutine (selectionRoutine);
selectionRoutine = null;
// Inform all listeners about the new selection
OnSelectionChanged.Invoke(new HashSet<GameObject>(selection);
}
private IEnumerator SelectionRoutine()
{
// Start with an empty selection
selection.Clear();
// This is ok in a Coroutine as long as you yield somewhere within it
while(true)
{
// Get the ray shooting forward from the camera at the mouse position
// for other inputs simply replace this according to your needs
var ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
// Check if you hit any object
if(Physics.Raycast(ray, out var hit, layerMask = includeLayers ))
{
// If so Add it once to your selection
if(!selection.Contains(hit.gameObject)) selection.Add(hit.gameObject);
}
// IMPORTANT: Tells Unity to "pause" here, render this frame
// and continue from here in the next frame
// (without this your app would freeze in an endless loop!)
yield return null;
}
}
}
Ofcourse you could do it directly in Update in this example but I wanted to provide it in a way where you can easily exchange the input method according to your needs ;)
From UX side you additionally might want to call a second event like OnSelectionPreviewUpdate or something like this every time you add a new object to the selection in order to be able to e.g. visualize the selection outcome.
I might have understood this wrong and it sounds like you rather wanted to get everything inside of a drawn shape.
This is slightly more complex but here would be my idea for that:
Have a dummy selection Rigidbody object that by default is disabled and does nothing
don't even have a renderer on it but a mesh filter and mesh collider
while you "draw" create a mesh based on the input
then use Rigidbody.SweepTestAll in order to check if you hit anything with it
Typed on smartphone but I hope the idea gets clear
I think I would try to create a PolygonCollider2D because it is quite simple comparing to creating a mesh. You can set its path (outline) by giving it 2D points like location of your pointer/mouse. Use the SetPath method for it. You can then use one of its methods to check if another point in space overlaps with that collider shape.
While the PolygonCollider2D interacts with 2D components you can still use its Collider2D.OverlapPoint method to check positions/bounds of your 3D objects after translating it to 2D space.
You can also use its CreateMesh method to create a mesh for drawing your selection area on the screen.
You can read more about the PolygonCollider2D here.
Hope it makes sens and hope it helps.

Adding Animation to Inputfield and save the transform

I wanted to add a simple search animation to my input field in my unity UI.
This is my InputField and the idea is, when I select it, it should expand slowly, and when I deselect it, it should shrink back to its normal form.
This is the rect Transform Component of this inputfield. I added to the input field and Event Trigger Component and an Animator. I created two Animations called SearchAnimation and DeselectAnimation and added them to my AnimationController called 'SearchController'.
This is how I designed my SearchController:
I set the Transitions between the defaultState and SearchAnimation to be listening to the SelectBool and DeselectBool (the name already describes its purpose).
Then I added the following script to my input Field so that those two booleans will be set accordingly to the event trigger:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class OnClickScript : MonoBehaviour {
Animator anim;
void Start()
{
anim = GetComponent<Animator>();
}
public void OnSelect()
{
anim.SetBool("SelectBool", true);
anim.SetBool("DeselectBool", false);
GetComponent<RectTransform>().sizeDelta = new Vector2(450, 50);
GetComponent<RectTransform>().localPosition.Set(-275, 0, 0);
}
public void OnDeselect()
{
anim.SetBool("DeselectBool", true);
anim.SetBool("SelectBool", false);
GetComponent<RectTransform>().sizeDelta = new Vector2(200, 50);
GetComponent<RectTransform>().localPosition.Set(-130, 0, 0);
}
}
But after the animation is played the inputfield is set back to its inital size and location. How do I fix this?
Easy way:
Create one more clips. The clip is made of only one key which is the Size Delta of the SelectedState. Then make this clip the animation clip of the state.
I put my test project here.
With above approach, once you want to change the size of default state and selected state, you will have to change all four animation clips manually.
Hard way
Using AnimationClip.SetCurve to create animation from the script. With this approach, you can create more maintainable animations. But it's not easy to create complex animations with scripts.
Suggestions:
Using Pivot:
In the script, you are changing local position to prevent the input field moving up. Instead of changing the local position value, you can set the pivot Y to 1 if you want the input field to expand downward.
Using trigger:
Instead of using two bool variables, you can simply use one Trigger to trigger the animation start and move to next state.

javafx: How can I put slider tick labels above its sliding track in horizontal slider?

I am trying to customize the horizontal slider such that its major tick labels appear above sliding track (by default they appear below sliding track) and major tick label values appear in reverse order. Do I need to use CSS somehow to display the labels above sliding track?
So far I have reversed the labels ordering using setLabelFormatter:
final double sliderMax = slider.getMax();
slider.setLabelFormatter(new StringConverter<Double>() {
#Override
public String toString(Double d) {
return String.valueOf((int)(sliderMax - d));
}
#Override
public Double fromString(String str) {
return (sliderMax - Double.parseDouble(str));
}
});
Any pointers will be helpful.
This is certainly possible with CSS.
This will only work for horizontal Slider though. Theoretically there is the pseude CSS class horizontal on the Slider which could be used to restict the CSS to horizontal Sliders, but I was unable to test this, maybe you can make it work.
Check out the CSS-Refrence and inspect your application with ScenicView.

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.

How to slide a listview by interacting with seekbar

I have a listview and a seekbar, i want the listview to react accroding to the interaction with the seekbar.
For ex :- if the seekbar is dragged to either ends, the listview should automatically go up or down.
Any help is greatly appreciated.
You should be able to use the list view method:
smoothScrollToPosition(int)
and OnSeekBarChangeListener
Set an OnSeekBarChangeListener on your seekbar.
In onProgressChanged add code using smoothScrollToPosition to update the listvew position.
Something like this:
public void onProgressChanged (SeekBar seekBar, int progress, boolean fromUser) {
listView.smoothScrollToPosition(progress); //assuming you have a 1 seekbar position for each row in the list, otherwise you'll need to do some calculation to compute the new list view position.
}
alternatively, you could use the onStopTrackingTouch method if you only want to scroll to the final position the user touched in the seekbar.