I'd like to be able to override a checkbox with code similar to this:
f:checkbox {
title = "Some Title",
value = bind { key = 'someProperty',
transform = function(value, fromModel)
if not fromModel then
if not userReallyWantsToDoThis() then
value = not value
end
end
return value
end,
},
checked_value = true,
unchecked_value = false,
}
This works so far as setting the bound property goes, but the visual representation doesn't change if the transform is perfomed (e.g. if the incoming and outgoing "transform" values differ). So, even if the user changed his mind, it still looks like the override didn't happen (i.e. the box is still checked/unchecked as the case may be).
Has anyone else seen this behavior? Any work-arounds? BTW, this is on LR 3.6 -- I haven't tried any others.
-Don
The "not fromModel" clause is needed to distinguish between a user "click" and, say, the initialization of the checkbox by Lightroom. But, that's neither here nor there -- the transform function is only invoked once per mouse-click.
As an aside, before implementing this functionality in the transform function (where it belongs, I think), I had tried doing the same thing using code in a property observer to do the override. However, when I used that mechanism I had the exact opposite problem: the UI checkbox looked the way I expected it to, but the property value (in the plug-in's preferences table) didn't "stick".
-Don
I couldn't get your approach to work either. But I was able to use a property-table observer to make it work:
local Require = require 'Require'.path ("../common")
local Debug = require 'Debug'
require 'strict'
local LrBinding = import 'LrBinding'
local LrDialogs = import 'LrDialogs'
local LrFunctionContext = import 'LrFunctionContext'
local LrRecursionGuard = import 'LrRecursionGuard'
local LrView = import 'LrView'
local bind = LrView.bind
local f = LrView.osFactory()
local recursionGuard = LrRecursionGuard ("check")
local function userReallyWantsToDoThis (prop, key, newValue)
recursionGuard:performWithGuard (function ()
if "ok" == LrDialogs.confirm ("Do you want to do this?") then
prop.check = newValue
else
prop.check = not prop.check
end
end)
end
LrFunctionContext.callWithContext ("", function (context)
local prop = LrBinding.makePropertyTable (context)
prop.check = false
prop:addObserver ("check", userReallyWantsToDoThis)
LrDialogs.presentModalDialog {title = "Test",
contents = f:column {bind_to_object = prop,
f:checkbox {title = "Some Title",
checked_value = true, unchecked_value = false,
value = bind {key = "check"}}}}
end)
Thanks for looking at this, John. Your solution is pretty much what I started with. All looked like it was going peachy until I reloaded my plug-in, then the value was back to the pre-override state. Weird, but (I swear!) true. I'm trying to do this in the plug-in manager dialog, against the plug-in's preferences table (if it makes a difference).
-Don
[[Apologies if anyone has seen this response previously. Responded by email and it appears that never made it through to the forums.]]
Hi Don,
I was trying to work out the logic behind this. Let me know whether you agree with the reasoning here.
The value is being bound to propertyTable.someProperty. Every time this property's value changes it then executes all observers including the transform function to convert the value into the value needed by the checkbox.
In your original transform function it was synchronously resetting that exact same bound value to a different value. There are only two ways I think that situation can be handled:
1) when you set the property value from within the transform function, recursively call all observers (and thus the transform function) again before setting the value
2) suspend all observers for the property value during the transform function to avoid getting into a recursive loop
Looks like the team have gone with option 2) here.
So to me this behaviour makes sense as otherwise it would be difficult to impossible to commit the property table processing with a consistent state.
If above holds true it is possible that you don't even need the sleep() call in your async task. Setting the property value from within the async task might be enough to allow the original property table observer to commit the value in a consistent state. If the sleep() proves necessary then people who make extensive use of async tasks will need to be very careful when setting property table entries, lest they encounter intermittent issues where observers don't fire because of this very situation.
Did that make sense?
Matt
Hi Matt,
I suspect your post didn't appear because I deleted the post you were replying to. Sorry about that. ![]()
Anyway, after I posted that I had success setting the property in an async task, further testing proved the results were inconsistent. I don't remember the specifics now -- I just know that I couldn't trust the behavior to provide consistent results, so I tossed the code, and my post.
Perhaps I'll revisit this later, but for now I'm just popping a warning when the user changes the setting (and popping another warning if he decides to change it back -- yuck). At least this way, what you see is what you'll get.
Thanks for looking into this.
-Don
North America
Europe, Middle East and Africa
Asia Pacific