I am new in kotlin and i don't understand what is wrong, when i do a "CLICK" in the imageView (val_jugadorX.alpha = 0.5f) i can't unselect it again (val_jugadorX.alpha = 1f)
fun comprobarSeleccion(val_jugadorX: ImageView): Boolean{
// si el boleano es false, no está seleccionado
// si el boleano es verdadero, está seleccionado
var devolver: Boolean = false
if (val_jugadorX.alpha == 1f){
val_jugadorX.setOnClickListener {
val_jugadorX.alpha = 0.5f
println("Ahora está seleccionado")
devolver = true
}
}else if(val_jugadorX.alpha== 0.5f) {
val_jugadorX.setOnClickListener{
val_jugadorX.alpha = 1f
println("Ahora no está selccio")
devolver = false
}
}
return devolver
}
You're setting a click listener based on the ImageView's current alpha, and that click listener changes the alpha. But it doesn't call comprobarSeleccion again to set a new click listener based on its new alpha. You probably want to do something like this:
fun comprobarSeleccion(val_jugadorX: ImageView): Boolean{
// si el boleano es false, no está seleccionado
// si el boleano es verdadero, está seleccionado
var devolver: Boolean = false
if (val_jugadorX.alpha == 1f){
val_jugadorX.setOnClickListener {
val_jugadorX.alpha = 0.5f
println("Ahora está seleccionado")
devolver = true
// now the alpha has changed, we need a new click listener
comprobarSeleccion(val_jugadorX)
}
} ...
}
Although something like this might be better (maybe set up during initialisation):
someImageView.setOnClickListener {
with(someImageView) {
val selected = alpha == 1f
// update display
alpha = if (selected) 0.5f else 1f
// handle the selection change event
setSelected(selected)
}
}
That way you just set a click listener once, and it handles things depending on its current state. By having a separate setSelected function, you can tell another part of the app that this selection state has changed:
var devolver = false
fun setSelected(selected: Boolean) {
println(if (selected) "Ahora está seleccionado" else "Ahora no está selccio")
devolver = selected
// any other stuff that needs to happen because of the selection change
}
That fixes your other problem - in your example, devolver is a local variable inside comprobarSeleccion. You set it to false, then return it (i.e. return false). If a click listener changes it later, nothing can access it. Next time you call comprobarSeleccion you get a new devolver set to false again.
By making it a top-level variable, your setSelected function can change it (or your click listener can if you want) and everything else can see its current value.
Related
Currently I'm working on a game like Mau-Mau (card game). The inner workings of the game do their job, just like they should. But for some reason when the player is able to lay several cards the game only detects a mouse click on the first card which can be layed. The mouse clicks are detected like this:
private var screenX = -1
private var screenY = -1
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
if (button == Input.Buttons.LEFT) {
this.screenX = screenX
this.screenY = screenY
}
return false
}
fun isCardClicked(card: Card): Boolean {
if (card.getSprite().boundingRectangle.contains(screenX.toFloat(), (Gdx.graphics.height - screenY).toFloat())) {
screenX = -1
screenY = -1
return true
}
screenX = -1
screenY = -1
return false
}
The method isCardClicked() is called in my Game class:
while (cardIterator.hasNext() && !cardLayed) {
val card = cardIterator.next()
if (player.isPlayable(card, deliCard)) {
if (inputHandler.isCardClicked(card)) {
println("player: $card")
deliStack.take(card)
cardIterator.remove()
cardLayed = true
changeTurns()
}
}
}
Does anybody know why I'm only able to lay the first layable card but not every other layable card in the player hand?
fun isCardClicked(card: Card): Boolean {
screenX = -1
screenY = -1
return card.getSprite().boundingRectangle.contains(screenX.toFloat(), (Gdx.graphics.height - screenY).toFloat())
}
You're resetting screenX and screenY to -1 before checking any card. So only the card overlapping that point will ever return true, not the card under the click point.
And a side note...with Kotlin you don't have to manually use the iterator to be able to find and remove an item. Your loop could be replaced with:
val removedCard = cardList.removeFirstOrNull { card ->
player.isPlayable(card, deliCard) && inputHandler.isCardClicked(card)
}
if (removedCard != null) {
println("player: $card")
deliStack.take(card)
changeTurns()
}
I've been trying out Tornadofx. trying to create a custom title-bar, here's the code I'm currently trying
fun main(args: Array<String>) {
launch<MyApp>(args)
}
class MyApp : App(Title::class) {
override fun start(stage: Stage) {
stage.initStyle(StageStyle.UNDECORATED)
stage.minWidth = 600.0
stage.minHeight = 450.0
stage.isMaximized = false
super.start(stage)
}
}
class Title : View() {
private var xOffset = 0.0
private var yOffset = 0.0
private var screenBounds: Rectangle2D = Screen.getPrimary().visualBounds
private var originalBounds: Rectangle2D = Rectangle2D.EMPTY
init {
primaryStage.isMaximized = false
}
override val root = borderpane {
onMousePressed = EventHandler { ev ->
xOffset = primaryStage.x - ev.screenX
yOffset = primaryStage.y - ev.screenY
}
onMouseDragged = EventHandler { ev ->
primaryStage.x = xOffset + ev.screenX
primaryStage.y = yOffset + ev.screenY
}
center = label("Forms")
right = hbox {
button("Mi") {
action {
with(primaryStage) { isIconified = true }
}
}
button("Ma") {
action {
if (primaryStage.isMaximized) {
with(primaryStage) {
x = originalBounds.minX
y = originalBounds.minY
width = originalBounds.width
height = originalBounds.height
isMaximized = false
}
text = "Ma"
} else {
with(primaryStage) {
originalBounds = Rectangle2D(x, y, width, height)
x = screenBounds.minX
y = screenBounds.minY
width = screenBounds.width
height = screenBounds.height
isMaximized = true
}
text = "Re"
}
}
}
button("X") {
action {
app.stop()
println("exiting")
exitProcess(0)
}
}
}
}
}
the following work without problems
close
maximize, restore
restored window minimized, then open from taskbar
but when a maximized window is minimized to taskbar, then open from taskbar, it goes full screen(taskbar is hidden)
how do i fix this behavior, is there any part of my code that is wrong, needs change, or in need of any inclusions?
my configuration is Windows 10 64bit, Java 11.0.2, Kotlin 1.4.21, JavaFx 11.0.2, TornadoFx 1.7.20
I think this is a general problem in JavaFX (I mean not specific with TornadoFX).
The root cause for this is because of setting the maximized property of stage to true. Not sure what JavaFX internally does, but when you open the window from task bar and if the maximized value is true, then it renders in full screen mode.
You can fix this in two ways.
Approach #1:
When the window is opened from task bar, the iconfied property will turn off, set the stage dimensions again to screen bounds if maximized is true.
primaryStage.iconifiedProperty().addListener((obs,old,iconified)->{
if(!iconified && primaryStage.isMaximized()){
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
}
});
Approach #2:
Don't rely on the maximized property of the Stage. I believe you need that property to toggle the window dimensions. So instead maintain a instance variable to handle that.
boolean maximized = false;
ma.setOnAction(e -> {
if (maximized) {
// Set stage to original bounds
maximized = false;
ma.setText("Ma");
} else {
// Set stage to screen bounds
maximized = false;
ma.setText("Re");
}
});
A full working demo is below with both the approaches. You can decide which way to go based on your other requirments.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class UndecoratedWindowFullScreenDemo extends Application {
private double xOffset = 0.0;
private double yOffset = 0.0;
private Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
private Rectangle2D originalBounds = Rectangle2D.EMPTY;
private boolean maximized = false;
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color:pink;");
Scene scene = new Scene(root, 600, 450);
primaryStage.setScene(scene);
Label label = new Label("Forums");
Button mi = new Button("Mi");
Button ma = new Button("Ma");
Button x = new Button("X");
HBox pane = new HBox(mi, ma, x);
pane.setPadding(new Insets(3));
pane.setSpacing(5);
root.setCenter(label);
root.setRight(pane);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMinWidth(600);
primaryStage.setMinHeight(450);
primaryStage.setMaximized(false);
primaryStage.show();
root.setOnMousePressed(e -> {
xOffset = primaryStage.getX() - e.getScreenX();
yOffset = primaryStage.getY() - e.getScreenY();
});
root.setOnMouseDragged(e -> {
primaryStage.setX(xOffset + e.getScreenX());
primaryStage.setY(yOffset + e.getScreenY());
});
mi.setOnAction(e -> primaryStage.setIconified(true));
/* Use this approach if you want to go with the Stage maximized property */
// approach1(primaryStage, ma);
/* Use this approach if you want to avoid Stage maximized property and maintain a instance variable */
approach2(primaryStage, ma);
}
private void approach1(Stage primaryStage, Button ma) {
primaryStage.iconifiedProperty().addListener((obs, old, iconified) -> {
if (!iconified && primaryStage.isMaximized()) {
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
}
});
ma.setOnAction(e -> {
if (primaryStage.isMaximized()) {
primaryStage.setX(originalBounds.getMinX());
primaryStage.setY(originalBounds.getMinY());
primaryStage.setWidth(originalBounds.getWidth());
primaryStage.setHeight(originalBounds.getHeight());
primaryStage.setMaximized(false);
ma.setText("Ma");
} else {
originalBounds = new Rectangle2D(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight());
primaryStage.setX(screenBounds.getMinX());
primaryStage.setY(screenBounds.getMinY());
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
primaryStage.setMaximized(true);
ma.setText("Re");
}
});
}
private void approach2(Stage primaryStage, Button ma) {
ma.setOnAction(e -> {
if (maximized) {
primaryStage.setX(originalBounds.getMinX());
primaryStage.setY(originalBounds.getMinY());
primaryStage.setWidth(originalBounds.getWidth());
primaryStage.setHeight(originalBounds.getHeight());
maximized = false;
ma.setText("Ma");
} else {
originalBounds = new Rectangle2D(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight());
primaryStage.setX(screenBounds.getMinX());
primaryStage.setY(screenBounds.getMinY());
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
maximized = true;
ma.setText("Re");
}
});
}
}
There are two changes that were needed to solve the problem
The actual problem was that if isMaximized is set to true the app goes full screen when being open from task(minimized) even though isFullScreen property is separately available
Adding a maximized property listener so that we can invalidate if the isMaximized were to be ever modified by other means(like double clicking on title bar in Linux etc)
// CHANGE 1
stage.maximizedProperty().addListener { _, _, newValue ->
if (newValue) stage.isMaximized = false
}
by having a separate maximized instead of using isMaximized
// CHANGE 2
private var maximized: Boolean = false // <- here
if (maximized) { // <- here
// restore the window by setting bounds of original size
maximized = false // <- here
text = "Ma"
} else {
// maximize window by setting bounds from screen size
maximized = true // <- and here
text = "Re"
}
Bonus : use isFocusTraversable = false to make buttons that don't focus with keyboard traversal
Final solution
fun main(args: Array<String>) {
launch<MyApp>(args)
}
class MyApp : App(Window::class, Styles::class) {
override fun start(stage: Stage) {
stage.initStyle(StageStyle.UNDECORATED)
stage.minWidth = 600.0
stage.minHeight = 450.0
stage.width = 600.0
stage.height = 450.0
// CHANGE 1
stage.maximizedProperty().addListener { _, _, newValue ->
if (newValue) stage.isMaximized = false
}
stage.isMaximized = false
super.start(stage)
}
}
class Window : View() {
override val root = borderpane {
top = Title().root
}
}
class Title : View() {
// CHANGE 2
private var maximized: Boolean = false // <- here
private var xOffset = 0.0
private var yOffset = 0.0
private var screenBounds: Rectangle2D = Screen.getPrimary().visualBounds
private var originalBounds: Rectangle2D = Rectangle2D.EMPTY
init {
primaryStage.isMaximized = false
}
override val root = hbox {
hgrow = Priority.ALWAYS
onMousePressed = EventHandler { ev ->
xOffset = primaryStage.x - ev.screenX
yOffset = primaryStage.y - ev.screenY
}
onMouseDragged = EventHandler { ev ->
primaryStage.x = xOffset + ev.screenX
primaryStage.y = yOffset + ev.screenY
}
val l1 = hbox {
hgrow = Priority.ALWAYS
alignment = Pos.CENTER
label("Forms")
}
add(l1)
l1.requestFocus()
button("Mi") {
id = "min"
action {
with(primaryStage) { isIconified = true }
}
isFocusTraversable = false
}
button("Ma") {
id = "max"
action {
if (maximized) { // <- here
with(primaryStage) {
x = originalBounds.minX
y = originalBounds.minY
width = originalBounds.width
height = originalBounds.height
maximized = false // <- here
}
text = "Ma"
} else {
with(primaryStage) {
originalBounds = Rectangle2D(x, y, width, height)
x = screenBounds.minX
y = screenBounds.minY
width = screenBounds.width
height = screenBounds.height
maximized = true // <- and here
}
text = "Re"
}
l1.requestFocus()
}
isFocusTraversable = false
}
button("X") {
id = "close"
action {
app.stop()
println("exiting")
exitProcess(0)
}
isFocusTraversable = false
}
}
}
I'd like to implement a card style UI(as the screen shot), card is using a wxPanel as a container, a static text and a static bmp in the panel. the card will zoom in and color changed when mouse on it, when the mouse leave the card, size and color will recover as normal state. I add some logic in onEnter and onLeave of wxPanel to handle zoom and color change. the card works fine in most of cases, but if mouse move quickly, multiple cards will be selected. From output log, it seems the issue happens in OnLeave, !GetClientRect().Contains(event.GetPosition()) = false, even the mouse actually has been out of the rect of the card. how can the card work as expected even mouse move quickly?
enter image description here
void TypeCard::OnEnter(wxMouseEvent& event) {
wxRect rect = GetClientRect();
wxPoint pnt = event.GetPosition();
TRACELOG_WARNING("###### On Enter left=%d top=%d width=%d height=%d", rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
TRACELOG_WARNING("&&&&&& On Enter x=%d y=%d", pnt.x, pnt.y);
if (GetClientRect().Contains(event.GetPosition()) && !selected_ ) {
TRACELOG_WARNING("&&&&&&On Enter = true");
if (m_bZoomEnabled) {
ZoomCard(zoom_in, 5, 5);
}
selected_ = true;
} else {
TRACELOG_WARNING("*****************************************************On Enter = false selected_ = %d", (int)selected_ );
}
// have_focus_ = true;
Refresh(false);
Update();
event.Skip();
}
void TypeCard::OnLeave(wxMouseEvent& event) {
wxRect rect = GetClientRect();
wxPoint pnt = event.GetPosition();
TRACELOG_WARNING("###### On OnLeave left=%d top=%d width=%d height=%d", rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
TRACELOG_WARNING("&&&&&& On OnLeave x=%d y=%d", pnt.x, pnt.y);
// if (!GetClientRect().Contains(event.GetPosition())) {
if (!rect.Contains(pnt)) {
if (m_bZoomEnabled) {
ZoomCard(zoom_out, 5, 5);
}
selected_ = false;
} else {
TRACELOG_WARNING("*****************************************************On OnLeave = false");
}
if (!selected_) {
// have_focus_ = false;
Refresh(false);
Update();
}
event.Skip();
}
void TypeCard::OnPaint(wxPaintEvent& event) {
wxPaintDC dc(this);
wxColour colour = selected_ ? CARD_SELECT_BACKGROUND_COLOUR : COMPONENT_UNSELECT_BACKGROUND_COLOUR;
wxCursor cur = selected_ ? wxCURSOR_HAND : wxCURSOR_ARROW;
dc.SetPen(wxPen(colour));
dc.SetBrush(wxBrush(colour));
this->SetCursor(cur);
const wxWindowList& list = this->GetChildren();
for (wxWindowList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext()) {
wxWindow* current = node->GetData();
if (current) {
current->SetBackgroundColour(colour);
current->SetCursor(cur);
}
}
dc.DrawRectangle(0, 0, this->GetSize().GetWidth(), GetSize().GetHeight());
}
Is it possible that the axis scale outside the graph could be scale using the mouse event "mouse_down and hold" and move up or down in y-axis the same with the x-axis move left or right? ex. when I trigger MouseDownEvent and hold the x-axis scale 0.6 or at the space along with that scale and move it to the right, scale should scroll depend in the chartfraction? could you post an example? Thanks in advance!
Separately panning and zooming Y axises can be achieved using the mouse events of ZedGraph: MouseDownEvent, MouseMoveEvent, MouseUpEvent and MouseWheel events (credits go to a colleague of mine).
It works with multiple GraphPanes and multiple Y axises.
The MouseMoveEvent is used to shift the Min and the Max of an Y axis when the mouse is moved while its button is pressed. If not, it is used to get the reference of the Y axis object the mouse is hovering on.
The MouseDownEvent is used to initiate an axis pan operation.
The MouseWheel is used to perform a zoom on an Y axis.
And the MouseUpEvent is used to clean things when zooming and panning operations are finished.
Here is the code :
// The axis that is currently hovered by the mouse
YAxis hoveredYAxis;
// The graphpane that contains the axis
GraphPane foundPane;
// The scale of the axis before it is panned
double movedYAxisMin;
double movedYAxisMax;
// The Y on the axis when the panning operation is starting
float movedYAxisStartY;
void z_MouseWheel(object sender, MouseEventArgs e)
{
if (hoveredYAxis != null)
{
var direction = e.Delta < 1 ? -.05f : .05f;
var increment = direction * (hoveredYAxis.Scale.Max - hoveredYAxis.Scale.Min);
var newMin = hoveredYAxis.Scale.Min + increment;
var newMax = hoveredYAxis.Scale.Max - increment;
hoveredYAxis.Scale.Min = newMin;
hoveredYAxis.Scale.Max = newMax;
foundPane.AxisChange();
z.Invalidate();
}
}
bool z_MouseUpEvent(ZedGraphControl sender, MouseEventArgs e)
{
hoveredYAxis = null;
return false;
}
bool z_MouseMoveEvent(ZedGraphControl sender, MouseEventArgs e)
{
var pt = e.Location;
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (hoveredYAxis != null)
{
var yOffset = hoveredYAxis.Scale.ReverseTransform(pt.Y) - hoveredYAxis.Scale.ReverseTransform(movedYAxisStartY);
hoveredYAxis.Scale.Min = movedYAxisMin - yOffset;
hoveredYAxis.Scale.Max = movedYAxisMax - yOffset;
sender.Invalidate();
return true;
}
}
else
{
var foundObject = findZedGraphObject(null);
hoveredYAxis = foundObject as YAxis;
if (hoveredYAxis != null)
{
z.Cursor = Cursors.SizeNS;
return true;
}
else
{
if (z.IsShowPointValues)
{
z.Cursor = Cursors.Cross;
return false;
}
else
{
z.Cursor = Cursors.Default;
return true;
}
}
}
return false;
}
bool z_MouseDownEvent(ZedGraphControl sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (hoveredYAxis != null)
{
movedYAxisStartY = e.Location.Y;
movedYAxisMin = hoveredYAxis.Scale.Min;
movedYAxisMax = hoveredYAxis.Scale.Max;
return true;
}
}
return false;
}
This is a helper that factorizes a bit the object find operations of ZedGraph.
object findZedGraphObject(GraphPane pane = null)
{
var pt = zgc.PointToClient(Control.MousePosition);
if (pane == null)
{
foundPane = zgc.MasterPane.FindPane(pt);
if (foundPane != null)
{
object foundObject;
int forget;
using (var g = zgc.CreateGraphics())
if (foundPane.FindNearestObject(pt, g, out foundObject, out forget))
return foundObject;
}
}
return null;
}
If I understand your question correctly, here's my response:
zedgraph has got an in-built function called "Pan", you could change the scale of x & y axis.
Place the cursor within the 'chart area'
Hold the 'ctrl' button & move the mouse towards x & y directions to change the scale.
you could get back to original state by 'Un-Pan' (Context Menu)
Cheers..:)
Do You want to create a ScrollBar?
zedGraphControl1.IsShowHScrollbar = true;
//Set borders for the scale
zedGraphControl1.GraphPane.XAxis.Scale.Max = Xmax;
zedGraphControl1.GraphPane.XAxis.Scale.Min = Xmin;
I need to have a balloon like message box that displays for a few seconds and then fades away (not disappears at once)
Please advise how to do this.
Thanks
Furqan
Try this:
Dim buttonToolTip As New ToolTip()
with buttonToolTip
.ToolTipTitle = "Button Tooltip"
.UseFading = True
.UseAnimation = True
.IsBalloon = True
.ShowAlways = True
.AutoPopDelay = 5000
.InitialDelay = 1000
.ReshowDelay = 500
.IsBalloon = True
.SetToolTip(Button1, "Click me to execute.")
End With
EDITED:
If you need a fading MessageBox, you can do this:
create a form that accepts a status and a text in constructor
swtich on status drawing correct icon and place text on form
create a timer and on Tick event check if it's time to close
if it's time to close call FadeForm method
Example:
public enum Status
{
None,
Error,
Question,
Warning,
Info
}
public class FadingForm: Form
{
dt: TDateTime;
public FadingForm(Status status, string msg)
{
this.lbl.Text = msg; // I imagine you have a Label named lbl
switch (status)
{
case Warning:
img.Image = warning; // I imagine you have a PictureBox named img
break;
case ...
}
dt = DateTime.Now;
tmr.Enabled = true;
}
public void Tmr_Tick()
{
if (DateTime.Now - dt) > limit
{
FadeForm(10); //Just an example
Close();
}
}
public void FadeForm(byte NumberOfSteps)
{
float StepVal = (float)(100f / NumberOfSteps);
float fOpacity = 100f;
for (byte b = 0; b < NumberOfSteps; b++)
{
this.Opacity = fOpacity / 100;
this.Refresh();
fOpacity -= StepVal;
}
}
}
I think this behavior depends on users windows settings, if you want to achieve this you can code a balloon message your self and decrease its opacity before closing it completely.