addObserver doesn't work in the presentFloatingDialog

New Here ,
Jun 23, 2018 Jun 23, 2018

Copy link to clipboard

Copied

local function showCustomDialogWithObserver()

LrFunctionContext.callWithContext("main", function(context)

        local body = LrView.osFactory()

        local userTable= LrBinding.makePropertyTable(context);

        userTable.mobile = nil;

        userTable.verificationCode = nil;

        userTable.taskCount = 0;

        local function myCalledFunction ()

            logger:info("taskCount change")      //not print

        end

        userTable:addObserver("taskCount", myCalledFunction)

        local d = body:group_box {

            bind_to_object = userTable,

            font = "<system>",

            title = "login",

            show_title = true,

            height = 500,

            width = 500,

            body: column{

                margin = 100,

                body: row{

                    body: static_text{

                        title = "mobile:",

                    },

                    body: edit_field{

                        alignment = "left",

                        value = LrView.bind "mobile"

                    },

                },

                body: row{

                    body: static_text{

                        title = "password:",

                    },

                    body: edit_field{

                        alignment = "left",

                        value = LrView.bind "verificationCode"

                    }

                },

                body: row{

                    bind_to_object = userTable;

                    body: push_button{

                        title = "login",

                        enabled = true,

                        action = function()

                            userTable.taskCount = 4

                            logger:info(userTable.taskCount)

                            -- sendVerificationCode(userTable.mobile)

                        end

                    }

                }

            }

        }

        local c = body: row{

            body: tab_view {

                body: tab_view_item{

                    title = "account",

                    identifier = "account",

                    d,

                },

                body: tab_view_item{

                    title = "sync",

                    identifier = "sync"

                }

            }

        }

        LrDialogs.presentFloatingDialog(_PLUGIN, {

            title = 'xxpie',

            contents = c

         })

    end)

end

showCustomDialogWithObserver()

When login was clicked, the addObserver did not observe the change in taskCount, and taskCount change was not printed

TOPICS
SDK

Views

385

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

LEGEND , Jun 23, 2018 Jun 23, 2018
This took me quite a while to figure out what was going on! You're stumbling over obscure behavior of LR tasks, function contexts, and presentFloadingDialog, all of which are poorly documented.  Here's what's happening:1. callWithContext() creates a context and passes it to your main function.2. Your main function calls presentFloatingDialog() to create the floating dialog, which will continue to execute until it is closed.  3. presentFloatingDialog() returns immediately to your main function, w...

Likes

Translate

Translate
LEGEND ,
Jun 23, 2018 Jun 23, 2018

Copy link to clipboard

Copied

This took me quite a while to figure out what was going on! You're stumbling over obscure behavior of LR tasks, function contexts, and presentFloadingDialog, all of which are poorly documented.  Here's what's happening:

1. callWithContext() creates a context and passes it to your main function.

2. Your main function calls presentFloatingDialog() to create the floating dialog, which will continue to execute until it is closed. 

3. presentFloatingDialog() returns immediately to your main function, while the dialog continues to execute in another task.

4. Your main function exits, returning to callWithContext().

5. callWithContext() exits, destroying the context and everything attached to the context, which includes the the property table.

6. The floating dialog continues to execute, but since the binding between the observer function and the property table has been destroyed along with its context, the observer function is never called.

The fix involves just a couple of lines. You should start your main function in a separate task using postAsyncTaskWithContext() instead of callWithContext(), and the main function should wait until the floating dialog closes:

LrFunctionContext.postAsyncTaskWithContext("main", function(context)

    ...

    local floatingDialogClosed = false

    LrDialogs.presentFloatingDialog (_PLUGIN, {

        title = 'xxpie', contents = c,

        windowWillClose = function ()

            floatingDialogClosed = true end})

    while not floatingDialogClosed do

        LrTasks.sleep (0.5)

        end

While polling for an event offends our delicate architectural sensibilities, it's often necessary in LR, and done carefully, it won't have any impact on performance.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jun 25, 2018 Jun 25, 2018

Copy link to clipboard

Copied

I tried it with your solution, but the observation function is executed only on the first click, and not on the second

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 25, 2018 Jun 25, 2018

Copy link to clipboard

Copied

If you change this line:

userTable.taskCount = 4

to this:

userTable.taskCount = userTable.taskCount + 1

then the observer will be called on each click. Evidently, the observer is called only when the value actually changes.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jun 25, 2018 Jun 25, 2018

Copy link to clipboard

Copied

Thanks for your help

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jun 25, 2018 Jun 25, 2018

Copy link to clipboard

Copied

Can the observation function change the content of the presentFloatingDialog? For example, I click the "login" button, I want to change the body:column of d

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 25, 2018 Jun 25, 2018

Copy link to clipboard

Copied

In general, plugins can only make a limited number of changes to the controls being displayed. They can change the properties that are bound with bind().  And one trick is to place two or more controls on top of each over with f:view {place = "overlapping"}, and then to change the "visible" properties so that one of the controls is visible and the others are not.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 26, 2018 Jun 26, 2018

Copy link to clipboard

Copied

I forgot another technique: When the plugin wants to change the controls (LrView objects) being displayed, it can exit the dialog and construct a new one. A plugin can exit a modal dialog using LrDialogs.stopModalWithResult(); it can exit a floating dialog using the "close" function passed to the onShow function passed to presetFloatingDialog().

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Oct 20, 2021 Oct 20, 2021

Copy link to clipboard

Copied

Hello @johnrellis, I've tried your suggestion but seems like i miss something. can you please verify below my code. It helps me a lot. I use presentFloatingDialog dialog and wanted to close on button click but stopModalWithResult not working.

 

local LrFunctionContext = import 'LrFunctionContext'
local LrDialogs = import 'LrDialogs'
local LrView = import 'LrView'

MyCode = {}

function MyCode.MyDialog()
	LrFunctionContext.callWithContext("MyDialog", function(context)
        local MainDialog 
	    local f = LrView.osFactory()
		local c = f:column {											
			f:push_button {
					title = 'I should close the dialog too!',
					action = function(dialog)
                        LrDialogs.stopModalWithResult(dialog, 'cancel');
					end,
			},															
		}
	
	MainDialog = LrDialogs.presentFloatingDialog(_PLUGIN,
		{
			contents = c,
            onShow = function(close)
            end
        })
	end) -- end main function

end

-- Now display the dialogs
MyCode.MyDialog()

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 20, 2021 Oct 20, 2021

Copy link to clipboard

Copied

LATEST

LrDialogs.stopModalWithResult() only works with modal dialogs created by LrDialogs.presentModalDialog().  To close a floating dialog created by LrDialgos.presentFloatingDialog(), you need to save away the "close" function passed to the "onShow" function when the dialog is first displayed, and then invoke close().  

 

Here's your sample code suitably modified showing how this works:

local LrFunctionContext = import 'LrFunctionContext'
local LrDialogs = import 'LrDialogs'
local LrView = import 'LrView'

MyCode = {}

function MyCode.MyDialog ()
    LrFunctionContext.callWithContext ("MyDialog", function (context)
        local close
        local f = LrView.osFactory()
        local c = f:column {                                            
            f:push_button {title = 'I should close the dialog too!',
                action = function (dialog) close(); end}}
        local MainDialog = LrDialogs.presentFloatingDialog (_PLUGIN, {
            contents = c,
            onShow = function (windowFuncs) close = windowFuncs.close end})
        end) -- end main function
    end

    -- Now display the dialogs
MyCode.MyDialog ()

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines