Click on Sprite only gets detected for the first Sprite (LibGDX) - kotlin

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()
}

Related

Kotlin ListView IndexOutOfBoundsException

(new to kotlin) I'm making my own music app but i'm having an error that i don't understand :
in the bit of code where i try to access a random view (shuffle), i get the java.lang.IndexOutOfBoundsException: Index:96 Size:11.
96 in this example is the view in the listview that i'm trying to access.
It happens in this line : var iView = listViewMusic.get(idShuffle)
Same if i use listViewMusic[idShuffle]
EDIT : Turns out that 11 is only the 11 visible items on screen at any given moment, even if the list contains hundreds of items. When i use listViewMusic.smoothscrolltoposition(idShuffle) it works, but the size of 11 now relates to the 11 on screen after the scrolling
The function playNext() inside the activity, called when clicking on the shuffle button:
fun playNext(){
try {
// Find the order of the next song to play
var idShuffle = musicAdapter!!.getIdofItem(listMusicShuffled.get(musicNextToPlay).title)
// Find the right record in the list
//var iView = listViewMusic[idShuffle]
//toastIt(applicationContext, "adapter count ${listViewMusic.adapter.count}")
//toastIt(applicationContext, "listview count ${listViewMusic.count}")
var iView = listViewMusic.get(idShuffle) //throws the error
// Play it
playOrPause(iView)
// Prepare the next track
musicNextToPlay += 1
if (musicNextToPlay >= listMusicShuffled.size) {
musicNextToPlay = -1
}
} catch (e:Exception) {toastException(applicationContext, "playnext", e) }
}
The part of the function onCreate that fills in the listViewMusic:
// Retrieve the data
val mediaStoreUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query(mediaStoreUri, null, "", null, null)
//Browse the data
listMusic = mutableListOf()
val listMusicJson = getMusicFromJson()
while (cursor!!.moveToNext()) {
val musicName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val musicArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val musicUrl = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val musicType:String =
if (musicArtist.contains("lmdmf", true)) { "LMDMF" }
else if (musicName.contains("slack", true)) { "SLACK" }
else { "MUSIC" }
listMusic.add(
MusicTrack(
musicName,
musicPlayCount,
musicUrl,
musicType
)
)
}
cursor.close()
musicAdapter = MusicAdapter(listMusic.sortedWith(compareBy<MusicTrack>{ it.title }).filter { it.type == "MUSIC"}.toMutableList())
listViewMusic.adapter = musicAdapter

TornadoFx Undecorated window goes fullscreen when restored from task bar

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
}
}
}

Detecting a change in a compound property

Objective-C would not allow you to run the following code:
myShape.origin.x = 50
This made it easy to detect changes in the origin, since someone using your class was forced to write myShape.origin = newOrigin, and thus you could easily tie in to the setter of this property.
Swift now allows you to perform the original, formerly-disallowed code. Assuming the following class structure, how would you detect the change to the origin in order to execute your own code (e.g. to update the screen)?
struct Point {
var x = 0
var y = 0
}
class Shape {
var origin: Point = Point()
}
Update: Perhaps I should have been more explicit, but assume I don't want to modify the Point struct. The reason is that Shape is but one class that uses Point, there may very well be hundreds of others, not to mention that the origin is not the only way a Point may be used.
Property observers (willSet and didSet) do fire when sub-properties of that property are changed. In this case, when the x or y values of the Point structure change, that property will be set.
Here is my example playground code:
struct Point : Printable
{
var x = 0
var y = 0
var description : String {
{
return "(\(x), \(y))";
}
}
class Shape
{
var origin : Point = Point()
{
willSet(newOrigin)
{
println("Changing origin to \(newOrigin.description)!")
}
}
}
let circle = Shape()
circle.origin.x = 42
circle.origin.y = 77
And here is the console output:
Changing origin to (42, 0)!
Changing origin to (42, 77)!
Doesn't this work?
class Shape {
var origin: Point {
willSet(aNewValueForOrigin) {
// pre flight code
}
didSet(theOldValueOfOrigin) {
// post flight code
}
}
}
Edit: revisited code and added name of arguments to reflect what to expect.
You can use Property Observers also works for structs
Link to the part on the ebook
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
Use didSet, e.g.,
struct Point {
var x = 0
var y: Int = 0 {
didSet {
println("blah blah")
}
}
}
class Shape {
var origin: Point = Point()
}
let s = Shape()
s.origin.y = 2

android expandableListView animate slide up?

I want to animate slide down and slide up on expandablelistview when I click the groupItem.Then I have finish the slide down.
public class ExpandAnimation extends Animation {
private static final String TAG = "ExpandAnimation";
private View mAnimatedView;
private LayoutParams mViewLayoutParams;
private int mMarginStart, mMarginEnd;
private boolean mIsVisibleAfter = false;
private boolean mWasEndedAlready = false;
/**
* Initialize the animation
* #param view The layout we want to animate
* #param duration The duration of the animation, in ms
*/
public ExpandAnimation(View view, int duration) {
setDuration(duration);
mAnimatedView = view;
mViewLayoutParams = (LayoutParams) view.getLayoutParams();
// if the bottom margin is 0,
// then after the animation will end it'll be negative, and invisible.
mIsVisibleAfter = (mViewLayoutParams.bottomMargin == 0);
mMarginStart = mViewLayoutParams.bottomMargin;
Log.i(TAG, "mMarginStart:>>>>>>>"+mMarginStart);
mMarginEnd = (mMarginStart == 0 ? (0- view.getHeight()) : 0);
Log.i(TAG, "mMarginEnd:>>>>>>>"+mMarginEnd);
view.setVisibility(View.VISIBLE);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
Log.i(TAG, "applyTransformation-->"+interpolatedTime);
if (interpolatedTime < 1.0f) {
// Calculating the new bottom margin, and setting it
mViewLayoutParams.bottomMargin = mMarginStart
+ (int) ((mMarginEnd - mMarginStart) * interpolatedTime);
// Invalidating the layout, making us seeing the changes we made
mAnimatedView.requestLayout();
// Making sure we didn't run the ending before (it happens!)
} else if (!mWasEndedAlready) {
mViewLayoutParams.bottomMargin = mMarginEnd;
mAnimatedView.requestLayout();
if (mIsVisibleAfter) {
mAnimatedView.setVisibility(View.GONE);
}
mWasEndedAlready = true;
}
}
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
Log.i(TAG, "getChildView");
#SuppressWarnings("unchecked")
String text = ((Map<String, String>) getChild(groupPosition,
childPosition)).get("child");
if (convertView == null) {
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.child, null);
}
View toolbar = convertView.findViewById(R.id.toolbar);
setAnimationView(toolbar);
((LinearLayout.LayoutParams) toolbar.getLayoutParams()).bottomMargin = -75;
toolbar.setVisibility(View.GONE);
ExpandAnimation expandAni = new ExpandAnimation(toolbar, 1000);
toolbar.startAnimation(expandAni);
TextView tv = (TextView) convertView.findViewById(R.id.childTo);
tv.setText(text);
return convertView;
}
But when I click the groupItem to collapse the group,it doesn't call the getChildView() method.So how can I to call the getChildView() and let it slide up?
I believe that you want to extend BaseExpandableListAdapter if you want to call (or #Override) getChildView.
http://developer.android.com/reference/android/widget/BaseExpandableListAdapter.html

Displaying a fading balloon type message box

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.