Changing observed property value in EventHandler changes Event properties - kotlin

I have a TableView with a column of ComboBox cells, created with ComboBoxTableCell.forTableColumn(). I'm very new to JavaFX. You may want to skip to "TLDR" at the bottom before going through all of it.
In onEditCommit() for the column, I launch an Alert to get confirmation ("Really ____?"). If it isn't confirmed, I want the cell's value to revert to what it was originally. The observed property is a SimpleStringProperty, and the table/row/bean type is a FooBar object. The table is backed by an ObservableList<FooBar>, retrieved using the Event object. This is the EventHandler, in kotlin:
EventHandler { ev ->
ev.newValue?.let { str ->
println("A old ${ev.oldValue} ===> new ${ev.newValue}")
list[ev.tablePosition.row]?.let { foobar ->
if (str != foobar.prop.value) {
val confirm = runDialog()
if (!confirm) foobar.prop.value = ev.oldValue
println("B old ${ev.oldValue} ---> new ${ev.newValue} "
+"| prop ${foobar.prop.value}")
}
}
}
}
The ComboBox has three options, "Alpha", "Beta", "Charlie". Here's what happens when I select the ComboBox and change the value, but then cancel in the confirm dialoge (the actual output is indented):
[ Box reads "Alpha"; select "Beta" ]
A old Alpha ===> new Beta
[ Cancel dialog... Box reads "Beta"
B old Alpha ---> new Beta | prop Alpha
[ Repeat by selecting "Charlie" ]
A old Alpha ===> new Charlie
[ Cancel dialog ]
B old Alpha ---> new Charlie | prop Alpha
[ Box reads "Charlie" ]
Notice that the property is being reset, but the cell contents aren't reflecting that. I thought this might be an issue with what happens when the handler returns -- ie., the commit happens afterward -- but using Platform.runLater() to set the property did not make a difference.
What did is intuiting that the edit is never committed, ie., foobar.prop.value is always the same, so setting it to oldValue doesn't change anything. However, the ComboBox is still out-of-sync with the property it is supposed to be an observer of.
This theory is supported by the fix, which is to stash oldValue and set foobar.prop to something new before running the dialog.
val orig = ev.oldValue
list[ev.tablePosition.row]?.let { foobar ->
if (str != foobar.format.value) {
// Set before dialog.
foobar.format.value = ev.newValue
val confirm = runDialog()
if (!confirm()) foobar.format.value = orig
But what's a bit mysterious here is why orig is necessary at all -- why not just foobar.format.value = ev.oldValue? Because ev.oldValue changes in between the third
last and last lines above; I'm guessing the Event object is actually being recycled and set with foobar.format.value. Or it could have to do with the dialog, which probably makes it a bone fide bug. Here's what happens using the fix:
[ Box reads "Alpha"; select "Beta" ]
A old Alpha ===> new Beta
[ Cancel dialog... Box reads "Alpha"
B old Alpha ---> new Beta | prop Alpha ]
So I am at least able to get what I want, but I am troubled by all this unexpected behaviour.
Here's what happens if, instead of using orig, I change that assignment to ev.oldValue (with no other changes):
[ Box reads "Alpha"; select "Beta" ]
A old Alpha ===> new Beta
[ Cancel dialog... Box reads "Beta" ]
B old Beta ---> new Beta | prop Beta
Back to the same problem.
(TLDR) This means changing foobar.prop.value in the handler has the side affect of changing ev.oldValue.
Confirmed with:
println("old ${ev.oldValue}")
foobar.prop.value = ev.newValue
println("old now ${ev.oldValue}")
Which I guess is the short version of this question. I'm sure I'm not the first to notice this. Is that behaviour expected, documented or easily explicable? I suppose there is a certain desirability in it, but it makes the Event object seem a bit shifty.
There's also the matter of the ComboBox falling out of sync with the value, which does not seem desirable at all.
This is openjfx 11 on x64 linux.

Related

Vuejs: vue-color is not changing picker location if linked v-model value is changed

I am using Sketch option of vue-color for color picking. It uses v-model to communicate color change as follows:
<sketch-picker v-model="colors" />
where colors is a data initialised as below:
colors: {hex: "#c0392b", a: 1}
When page is loaded color-picker is at expected position i.e. (hex: #c0392b). On changing picker position, colors value changed as expected.
Issue comes when I change colors value in a method like below:
this.colors.hex = "#ff00ff";
this.colors.a = 0.5;
In this case even though data colors value is changed(this is confirmed), color-picker is still pointing at old location i.e. #c0392b.
I don't think this is an expected behavior and possibly a bug. What should be way-around this issue?
Detecting changes in objects is a bit nuanced in Vue/JavaScript. See https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats and the deep option of watch https://v2.vuejs.org/v2/api/#vm-watch
You may simply need to use
this.colors = Object.assign({}, this.colors, { a: 0.5, hex: "#ff00ff" })

Why do I get error with show window in Rebol/Red?

When I click on button refresh, I get an error
v: [field1 "to refresh" field2 "to refresh"
button "refresh" [show v]]
view v
How can refresh v ?
Update: I don't have error but it doesn't refresh:
test: [
["a1" "b1"]
["a2" "b2"]
]
i: 1
v: layout compose [
field data test/:i/1 field data test/:i/2 button "refresh" [i: i + 1 show v]
]
view v
Show expects a face, not a block.
You want probably something along
v: layout [
f1: field "to refresh"
f2: field "to refresh"
button "refresh" [f1/text: f2/text show v ]
]
To see a change you should type something in the second input field before clicking on the button.
To make something (usefully) working with your update you can use
i: 1
sw: true
v: layout compose [
f1: field data test/:i/1
f2: field data test/:i/2
button "refresh" [
i: pick [1 2] sw: not sw
f1/text: test/:i/1
f2/text: test/:i/2
show v
]
]
show does not load the face definition block again, but the red-gui-system docs say
"Red/View will update both face and graphic objects in realtime as their properties are changed. This is the default behavior, but it can be switched off, when full control over screen updates is desirable. This is achieved by:
system/view/auto-sync?: off
When automatic syncing is turned off, you need to use show function on faces to get the graphic objects updated on screen."
I guess you can get something similar to your intention by destroying the old face object and building a new with the specification block.
test: [
["a1" "b1"]
["a2" "b2"]
]
i: 1
sw: true
vvw: layout v: [
field data test/:i/1
field data test/:i/2
button "refresh" [
i: pick [1 2] sw: not sw
unview vvw
view v
]
]
view vvw
Not sure what you mean by "refresh", but Red's GUI is reactive, you don't need to explicitly tell it to update.
If you just want to update the text data (increment its value) - here's a short working example:
view [f: field "42" button "increment" [f/data: f/data + 1]]
That is, f is assigned to the just created field. On button press - it receives new value, by acessing its data member.

showandwait changing variable value

I'm having a problem with a variable I'm using to track the status of a user activity. In a GUI I have a button that, on clicking the button launches a second GUI. In that GUI, the user can either complete the activity started in the first GUI or not.
If the user cancels the second GUI, then the idea is to go back to the first GUI, leaving all variables and lists with their current values. If the second GUI completes the activity of the first GUI, then all variables and lists should be reset.
To track this, I have a variable (Boolean complete) initially set to FALSE. In the second GUI, when the "OK" button is clicked (rather than the "Cancel" button), the second GUI calls a method in the first GUI, changing the value of "complete" to TRUE.
To see what the heck is going on, I have System.out.println at several points allowing me to see the value of "complete" along the way. What I see is this:
Launching first GUI - complete = FALSE
Launching second GUI - complete = FALSE
Clicking "OK" in second GUI - complete = TRUE
Second GUI closes itself, returning to complete first GUI activity
First GUI finishes activity with complete = FALSE
I'm assuming it is because I am launching the second GUI with a showandwait, and when the method containing the showandwait begins, the value of "complete" = FALSE. The value changes in the WAIT part of show and wait, then the method continues and that is where I get the value still being FALSE, though it was changed to TRUE.
Here is a summary of the code in question (if you need exact code, it's longer, but I can post on request):
completeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
try {
System.out.println("b4 calc = " + complete); // complete = FALSE
// all the code to create the calcStage
calcStage.showAndWait(); // second GUI, which calls a method in THIS
// class that changes complete to TRUE. That method
// (in THIS file) also has a println that shows the change.
getComplete(); // tried adding this method to check the value of
// "complete" after the change made by the calcStage
// (which calls a method in this same file)
System.out.println("Complete? " + complete);
// this shows complete = FALSE,
// though in the calcStage it was changed to TRUE
if (salecomplete) {
// code that should reset all variables and lists if the activity was completed
}
}
}
}
The question here is why does the second GUI successfully change the value of "complete", but when I return to the first GUI it still sees complete as FALSE? And how can I get around this?
Try having the controller of the second GUI calling a method in the first GUI's controller to modify that complete variable
For example:
// Code to handle the OK button being pressed
#Override
public void handle(ActionEvent t) {
// Do validation and work
//reference to the first controller object
firstController.setComplete(true);
}

input dialog box blender

How to make a simple entry dialog box (like in the image) in blender and processing the text entered through python.I am unable to find any good tutorial on this.
For the dialog box the answer from how to show a message from a blender script? might be a starting point.
But I think a better approach is integrating input into the panel like e.g.
To do this you have to add a StringProperty to your add-on and place it inside your panel (see Addon Tutorial for more information). The basic steps are:
def draw(self, context) :
col = self.layout.column(align = True)
col.prop(context.scene, "my_string_prop")
...
def register() :
bpy.types.Scene.my_string_prop = bpy.props.StringProperty \
(
name = "My String",
description = "My description",
default = "default"
)
...
def unregister() :
del bpy.types.Scene.my_string_prop
...
You can access the string by context.scene.my_string_prop
There is another mode to integrate input. When you add for example a text to your scene you can change the parameters after the operator has been called and see the changes immediately:
Changing Location will move the newly created text object at another place. I haven't worked with this but it should be similar to the code above.

Wix: How can I set, at runtime, the text to be displayed in VerifyReadyDlg?

After the user goes through the Setup Wizard, and makes a few choices, the usual thing is to display the VerifyReadyDlg to say "Are you ready to install?"
The built-in VerifyReadyDlg is static. It does not present a summary of the choices he made previously. I'd like to modify it so that it does.
How can I do that?
Example
"Static" text:
Intelligent text:
I don't believe I can modify the Control table in the MSI, because mods during the installation process are not allowed. I found MsiViewModifyInsertTemporary, but I don't think that will work either. The relevant row in the Control table is already present, and it contains static data. I want to modify the data, just before VerifyReadyDlg is displayed.
You may not be able to modify existing rows in the MSI tables, but you can insert new "temporary" rows.
So, in a custom action, at runtime, insert one or more temporary rows into the Control table. In Javascript, it looks like this:
var controlView = Session.Database.OpenView("SELECT * FROM Control");
controlView.Execute();
var record = Session.Installer.CreateRecord(12);
record.StringData(1) = "VerifyReadyDlg"; // Dialog_ - the dialog to mod
record.StringData(2) = "CustomVerifyText1"; // Control - any unique name will do
record.StringData(3) = "Text"; // Type
record.IntegerData(4) = 25; // X
record.IntegerData(5) = 70; // Y
record.IntegerData(6) = 320; // Width
record.IntegerData(7) = 65; // Height
record.IntegerData(8) = 2; // Attributes
record.StringData(9) = ""; // Property
record.StringData(10) = text1; // Text - the text to be displayed
record.StringData(11) = ""; // Control_Next
record.StringData(12) = ""; // Help
controlView.Modify(MsiViewModify.InsertTemporary, record);
controlView.Close();
You probably want that custom text to be displayed only upon INSTALL. In that case, add a condition, in the same way:
var controlCondView = Session.Database.OpenView("SELECT * FROM ControlCondition");
controlCondView.Execute();
record = Session.Installer.CreateRecord(4);
record.StringData(1) = "VerifyReadyDlg"; // Dialog_
record.StringData(2) = "CustomVerifyText1"; // Control_ - same name as above
record.StringData(3) = "Show"; // Action
record.StringData(4) = "NOT Installed"; // Condition
controlCondView.Modify(MsiViewModify.InsertTemporary, record);
controlCondView.Close();
The Msi constants are defined like this:
// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify =
{
Refresh : 0,
Insert : 1,
Update : 2,
Assign : 3,
Replace : 4,
Merge : 5,
Delete : 6,
InsertTemporary : 7, // cannot permanently modify the MSI during install
Validate : 8,
ValidateNew : 9,
ValidateField : 10,
ValidateDelete : 11
};
A couple notes:
The InstallText in the Control table is normally displayed. It can be customized with a .wxl file, inserting something like this:
<String Id="VerifyReadyDlgInstallText">Whatever.</String>
This results in a row in the Control table. But you cannot remove rows from the table at runtime.
If you choose the X,Y and Height,Width for your new custom text to be the same as for the static InstallText, the InstallText will be obscured.
It may seem counter-intuitive to use "NOT Installed" as the condition - but remember, this is the state of the world prior to running the Setup Wizard. If the MSI is Installed prior to running the Wizard, then you're probably not installing it, which means you don't need to display the choices being made in the wizard.
Of course you can add multiple controls this way. You could add multiple Text controls, or...You can add lines, buttons, checkboxes, whatever. For each one, you'll have to set the control type and geometry appropriately. Use Orca to examine the Control table to figure out how.
This approach works for any Dialog. You only have to be sure to run the custom action to insert the temp rows into the Control table, at some point in the InstallUISequence, before the Dialog is rendered.