Fragment BarChart in Modal is not updating - TornadoFX - kotlin

I've created a Fragment to hold a BarChart and a ScrollPane - the end result will be a scrollable histogram.
I'm creating these new fragments in a seperate modal using the openModal method.
The problem that i'm having is that the BarChart doesn't seem to be updating when I call my loadData method, as shown below:
class Histogram : Fragment() {
override val root = vbox{
hgrow = Priority.ALWAYS
vgrow = Priority.ALWAYS
style{
minWidth = 1280.px
minHeight = 180.px
}
hbox{
hgrow = Priority.ALWAYS
}
}
private val bar = ScrollBar()
private var barChart = barchart("bar", CategoryAxis(), NumberAxis()){
barGap = 0.0
categoryGap = -1.0
hgrow = Priority.ALWAYS
vgrow = Priority.ALWAYS
style{
minWidth = 640.px
minHeight = 240.px
maxHeight = 480.px
}
isLegendVisible = false
}
private val s = XYChart.Series<String, Number>()
init{
root.add(barChart)
root.add(bar)
}
fun loadData(h: AlignmentHistogram){
s.data.add(XYChart.Data<String, Number>("asd", 2))
barChart.title = h.rname
/* for(i in 0..MAX_DATASET_SIZE){
if(i > h.histogram.size){
break
}
val data = XYChart.Data<String, Int>((h.firstPosition + i + 1).toString(), (h.histogram[i]))
println(data)
s.data.add(data)
}*/
s.data.add(XYChart.Data<String, Number>("asasdd", 5))
s.name = h.rname
barChart.data.add(s)
}
}
AlignmentHistogram is just a POJO with the data for the histogram and a few other details.
I'm calling the fragment with the following code:
val stage = Stage()
stage.title = "${pointer.rname} - ${selectedFile.file.name}"
val view = Histogram()
view.loadData(h)
val parent = StackPane()
stage.scene = Scene(parent, 1280.0, 360.0)
view.openModal(StageStyle.UTILITY, Modality.NONE, false, stage, false, true)
The result of this is just an empty histogram in a new modal, despite calling barChart.data.add(s)
Any ideas? Thanks in advance!

Nevermind, I solved it by passing the POJO in as the scope and putting the set-up code into the init block.
This article helped with the scope part:
Tornadofx - How to pass parameter to Fragment on every instance

Related

How to monitor mouse point move entered a path in Jetpack Compose?

I got a problem on how to detect mouse pointer entered a path or reach in Jetpack Compose canvas, what I want is something like isPointInPath()in JavaScript: when my mouse moved into the react area in canvas, I want to change the react color into green.
My current code is blow, I wanted to make the rectangle change color like the white dot on the canvas in the code, I searched on google android doc, but I got nothing, any thing helpful will be appriciated:
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.input.pointer.pointerInput
data class PathProperties(val Angle: Float, val length: Float, val startPoint: Pair<Float, Float>)
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun customCanvas(){
var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
var previousPosition by remember { mutableStateOf(Offset.Unspecified) }
val randomAngle = listOf(45f, -45f)
var paths = remember { mutableStateListOf<Pair<Path, PathProperties>>() }
var currentPath by remember { mutableStateOf(Path()) }
var show by remember { mutableStateOf(false) }
Canvas(
modifier = Modifier
.fillMaxSize()
.background(color = Color.Gray)
.pointerInput(Unit) {
forEachGesture {
awaitPointerEventScope {
awaitFirstDown().also {
currentPosition = it.position
previousPosition = currentPosition
currentPath.moveTo(currentPosition.x, currentPosition.y)
val angle = randomAngle.random()
paths.add(Pair(currentPath, PathProperties(angle, 30f, Pair(currentPosition.x, currentPosition.y))))
}
}
}
}
.onPointerEvent(PointerEventType.Move) {
val position = it.changes.first().position
show = (position.x in 90f..110f) && position.y in 90f..110f
}
){
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
paths.forEach { it: Pair<Path, PathProperties> ->
rotate(it.second.Angle, it.second.startPoint.first, it.second.startPoint.second )
drawLine(
color = Color.Black,
start = Offset(it.second.startPoint.first, it.second.startPoint.second ),
end = Offset(it.second.startPoint.first + it.second.length, it.second.startPoint.second),
cap = StrokeCap.Round
)
// draw a widder line on canvas using drawLine
drawLine(
color = Color.White,
start = Offset(it.second.startPoint.first, it.second.startPoint.second ),
end = Offset(it.second.startPoint.first + it.second.length, it.second.startPoint.second),
strokeWidth = 10f,
cap = StrokeCap.Square
)
rotate(-it.second.Angle, it.second.startPoint.first, it.second.startPoint.second)
}
// draw a white dot example
drawCircle(
color = if (show) Color.Green else Color.White,
center= Offset(100f, 100f),
radius = 10f,
)
}
}
}

MPhiljay Barchart in android

In Barchart show x values from column belongs to one label more than 2 two but should be one.
viewModel.reportMounthData.observe(this, Observer { reportModelList->
val barChart = binding.idBarChartMounth
barChart.setScaleEnabled(false)
val entries: ArrayList<BarEntry> = ArrayList()
var i=0
reportModelList.forEach { reportModel->
entries.add(BarEntry(reportModel.YigilganBonus.toFloat(), i))
i++
}
val bardataset = BarDataSet(entries, "Oylik maoshlar miqdori")
val labels = ArrayList<String>()
reportModelList.sortedBy { it.oy }.forEach { reportModel ->
labels.add(mounthSalary(reportModel.oy))
}
val data = BarData(labels, bardataset)
barChart.data = data // set the data and list of labels into chart
barChart.setDescription("\uD83D\uDEE0 Oylik maosh statistikasi") // set the description
// bardataset.setColors(ColorTemplate.COLORFUL_COLORS)
barChart.animateY(2500)
})
enter image description here
it works perfectly until the values ​​reach 6. If there are more than 6, a defect may occur naturally. they said that the main reason for this is that it will not fit on the screen

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

Centering DataTip (target) on a ColumnChart in Flex 3

How can I align a DataTip to the vertical center of the corresponding column? I've tried creating a custom dataTipRenderer, but it seems to me that there I can only move the datatip relative to the target (the circle graphic). But that position's just fine, I'd like to move the target itself.
My last idea is to set the showDataTipTargets style of the chart to false and draw the targets within the custom dataTipRenderer. I consider this a dirty hack, so if there's anything more friendly I'd go with that. Plus, in this case, how can I tell the column center coordinates in the datatip renderer's updateDisplayList function?
Hope this snippet of code would help ...
package As
{
import flash.display.*;
import flash.geom.Point;
import mx.charts.*;
import mx.charts.chartClasses.DataTip;
import mx.charts.series.ColumnSeries;
import mx.charts.series.items.ColumnSeriesItem;
public class MyDataTip extends DataTip
{
private var _xBaseLine:int=0;
private var _yBaseLine:int=0;
private var myX = 0
private var myY = 0
public function MyDataTip()
{
super();
}
override public function set data(arg1:Object):void {
var sMessage:String;
var pt:Point;
var hitData:HitData = mx.charts.HitData(arg1);
var chartItem = ColumnSeriesItem(hitData.chartItem);
var renderer = chartItem.itemRenderer;
var series = ColumnSeries(hitData.element);
var colName = chartItem.element.name
var pft = chartItem.xValue
if(renderer != null) {
myX = ( renderer.width / 2 )
myY = ( renderer.height/ 2 ) + ( this.height)
}
super.data=arg1;
}
override public function move (x:Number, y:Number):void {
// Adjusted
var pointAdjusted:Point = new Point(x + _xBaseLine, y + _yBaseLine);
// Call the parent
super.move(pointAdjusted.x, pointAdjusted.y);
}
override protected function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w, h);
this.x = this.x + myX - 15
this.y = this.y + myY - 7
}
}
}
Cheers !! :)