The Inbox: Monticello-ct.712.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Monticello-ct.712.mcz

commits-2
Christoph Thiede uploaded a new version of Monticello to project The Inbox:
http://source.squeak.org/inbox/Monticello-ct.712.mcz

==================== Summary ====================

Name: Monticello-ct.712
Author: ct
Time: 14 January 2020, 1:58:10.168813 pm
UUID: c048116c-f5ca-8c4d-adab-34d19467565a
Ancestors: Monticello-cmm.708

Reuse ToolBuilder utility in MCTool >> #showModally

=============== Diff against Monticello-cmm.708 ===============

Item was changed:
  ----- Method: MCTool>>showModally (in category 'morphic ui') -----
  showModally
  modalProcess := Processor activeProcess.
  self window openInWorldExtent: self defaultExtent.
+ ToolBuilder default runModal: self window.
- [self window world notNil] whileTrue: [
- self window outermostWorldMorph doOneCycle.
- ].
  morph := nil.
  ^ modalValue!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Jakob Reschke
Okay for the time being and certainly not worse.

But eventually these dialogs ought to send a message when they are accepted, to continue the workflow.

<[hidden email]> schrieb am Di., 14. Jan. 2020, 13:58:
Christoph Thiede uploaded a new version of Monticello to project The Inbox:
http://source.squeak.org/inbox/Monticello-ct.712.mcz

==================== Summary ====================

Name: Monticello-ct.712
Author: ct
Time: 14 January 2020, 1:58:10.168813 pm
UUID: c048116c-f5ca-8c4d-adab-34d19467565a
Ancestors: Monticello-cmm.708

Reuse ToolBuilder utility in MCTool >> #showModally

=============== Diff against Monticello-cmm.708 ===============

Item was changed:
  ----- Method: MCTool>>showModally (in category 'morphic ui') -----
  showModally
        modalProcess := Processor activeProcess.
        self window openInWorldExtent: self defaultExtent.
+       ToolBuilder default runModal: self window.
-       [self window world notNil] whileTrue: [
-               self window outermostWorldMorph doOneCycle.
-       ].
        morph := nil.
        ^ modalValue!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Christoph Thiede

(Just merging two threads ... :-))


Hi Jakob,

yes, this is what I also mentioned in the other thread (attached below). I don't like the fact that at the moment, you may evaluate any code that raises an error and kills the modal loop.
You proposed using a promise - I never had the pleasure to meet them until today. Maybe you have a minimum working example for demonstrating your proposal? FutureNodes seem to be used very rarely in Trunk :-)

My first idea would have been something like this:
Of course, a semaphore might be better than the [Processor yield] doWhileTrue: ...
Model could override #windowIsClosing for signaling such a semaphore.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Mittwoch, 15. Januar 2020 08:00 Uhr
An: [hidden email]
Betreff: Re: [squeak-dev] The Inbox: Monticello-ct.712.mcz
 
Okay for the time being and certainly not worse.

But eventually these dialogs ought to send a message when they are accepted, to continue the workflow.

<[hidden email]> schrieb am Di., 14. Jan. 2020, 13:58:
Christoph Thiede uploaded a new version of Monticello to project The Inbox:
http://source.squeak.org/inbox/Monticello-ct.712.mcz

==================== Summary ====================

Name: Monticello-ct.712
Author: ct
Time: 14 January 2020, 1:58:10.168813 pm
UUID: c048116c-f5ca-8c4d-adab-34d19467565a
Ancestors: Monticello-cmm.708

Reuse ToolBuilder utility in MCTool >> #showModally

=============== Diff against Monticello-cmm.708 ===============

Item was changed:
  ----- Method: MCTool>>showModally (in category 'morphic ui') -----
  showModally
        modalProcess := Processor activeProcess.
        self window openInWorldExtent: self defaultExtent.
+       ToolBuilder default runModal: self window.
-       [self window world notNil] whileTrue: [
-               self window outermostWorldMorph doOneCycle.
-       ].
        morph := nil.
        ^ modalValue!




Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Montag, 13. Januar 2020 20:19 Uhr
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Closing modal dialogs...
 
Thiede, Christoph <[hidden email]> schrieb am Mo., 13. Jan. 2020, 13:10:

(Moreover, I never was really happy with the way to pause the UI process ... In particular, MC modal dialogs, which are not exclusive, don't need to decorate the whole UI process. Why can't these things work with the observer pattern instead?)

Promises can also be nice. Allows you to write the continuation next to the invocation.


Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Jakob Reschke
Am Mi., 15. Jan. 2020 um 09:13 Uhr schrieb Thiede, Christoph <[hidden email]>:

You proposed using a promise - I never had the pleasure to meet them until today. Maybe you have a minimum working example for demonstrating your proposal? FutureNodes seem to be used very rarely in Trunk :-)

I have no minimum example ready, but here is the outline:
- Add an instance variable for the promise to the model.
- Initialize with `Promise new` when the model is asked to open a view.
- Answer the promise to the open message.
- In the model's method that is invoked when the dialog is accepted, promise resolveWith: theResultOfWhatHappened
- In the method that opens the tool, take the promise and use whenResolved:, whenRejected: (handle the outcome) or then:ifRejected: (handle the outcome and answer another promise -- chaining). Do not attempt to return from the handler blocks, the method will have returned by the time they are evaluated.

Of course it is a totally different style of programming than with modal windows.

Similar alternative without promises:
- Instead of a promise, store a MessageSend in the model.
- In the client of the model, store all necessary state to continue in instance variables.
- Do not expect a return value from opening the view for the model.
- Have the model send the message to self upon continuation.



pastedImage.png (161K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Jakob Reschke
Since you mentioned the #future, I recently tried to wrap modal dialog (well not really, it is not exclusive, it just has a synchronous API to obtain the result) with a promise interface and came up with the following. If I thought it through correctly, it should not lose the continuation even when the UI process is terminated and replaced (via Ctrl+., abandon, for example). Haven't tested it yet, though. Would anyone like to review?

In the model...
selectedChangesWithTitle: titleString
| builder |
builder := ToolBuilder default.
view := builder open: self label: titleString.
promise ifNil: [promise := Promise new].
self future resumeModalRequest. "will resolve the promise"
^ promise

resumeModalRequest
view ifNil: [self error: 'Tool was not opened yet'].
promise ifNil: [self error: 'Tool was not opened with selectedChangesWithTitle:'].
[ToolBuilder default runModal: view]
ifCurtailed:
["Make sure we will notice in a new UI process when the view is closed."
self future resumeModalRequest].
"If we finally get here, the view was closed."
self accepted ifTrue: [promise resolve] ifFalse: [promise reject].

Am Mi., 15. Jan. 2020 um 21:32 Uhr schrieb Jakob Reschke <[hidden email]>:
Am Mi., 15. Jan. 2020 um 09:13 Uhr schrieb Thiede, Christoph <[hidden email]>:

You proposed using a promise - I never had the pleasure to meet them until today. Maybe you have a minimum working example for demonstrating your proposal? FutureNodes seem to be used very rarely in Trunk :-)

I have no minimum example ready, but here is the outline:
- Add an instance variable for the promise to the model.
- Initialize with `Promise new` when the model is asked to open a view.
- Answer the promise to the open message.
- In the model's method that is invoked when the dialog is accepted, promise resolveWith: theResultOfWhatHappened
- In the method that opens the tool, take the promise and use whenResolved:, whenRejected: (handle the outcome) or then:ifRejected: (handle the outcome and answer another promise -- chaining). Do not attempt to return from the handler blocks, the method will have returned by the time they are evaluated.

Of course it is a totally different style of programming than with modal windows.

Similar alternative without promises:
- Instead of a promise, store a MessageSend in the model.
- In the client of the model, store all necessary state to continue in instance variables.
- Do not expect a return value from opening the view for the model.
- Have the model send the message to self upon continuation.


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Christoph Thiede

Yes, this programming style sounds really interesting but totally different. :)

Funny, the only real sender of #future in my image comes from Squot :-)

Similar alternative without promises:
> - Instead of a promise, store a MessageSend in the model.
> - In the client of the model, store all necessary state to continue in instance variables.
> - Do not expect a return value from opening the view for the model.
> - Have the model send the message to self upon continuation.

And I would append observer pattern to this list, which is just a special case of your last point.

These proposals actually appear a bit easier and more convenient. I would like to use the same solution for DialogWindows as well which suffer from the same problem. But #inform: should be called as usual and hide any concurrent logic from the caller. How would you identify the object to resume after the information, if you called, for example, nil inform: #foo? I think the only way, without creating an extra DialogWindowModel, would be #spawnNewProcessDuring: ...

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Mittwoch, 15. Januar 2020 21:39:33
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] The Inbox: Monticello-ct.712.mcz
 
Since you mentioned the #future, I recently tried to wrap modal dialog (well not really, it is not exclusive, it just has a synchronous API to obtain the result) with a promise interface and came up with the following. If I thought it through correctly, it should not lose the continuation even when the UI process is terminated and replaced (via Ctrl+., abandon, for example). Haven't tested it yet, though. Would anyone like to review?

In the model...
selectedChangesWithTitle: titleString
| builder |
builder := ToolBuilder default.
view := builder open: self label: titleString.
promise ifNil: [promise := Promise new].
self future resumeModalRequest. "will resolve the promise"
^ promise

resumeModalRequest
view ifNil: [self error: 'Tool was not opened yet'].
promise ifNil: [self error: 'Tool was not opened with selectedChangesWithTitle:'].
[ToolBuilder default runModal: view]
ifCurtailed:
["Make sure we will notice in a new UI process when the view is closed."
self future resumeModalRequest].
"If we finally get here, the view was closed."
self accepted ifTrue: [promise resolve] ifFalse: [promise reject].

Am Mi., 15. Jan. 2020 um 21:32 Uhr schrieb Jakob Reschke <[hidden email]>:
Am Mi., 15. Jan. 2020 um 09:13 Uhr schrieb Thiede, Christoph <[hidden email]>:

You proposed using a promise - I never had the pleasure to meet them until today. Maybe you have a minimum working example for demonstrating your proposal? FutureNodes seem to be used very rarely in Trunk :-)

I have no minimum example ready, but here is the outline:
- Add an instance variable for the promise to the model.
- Initialize with `Promise new` when the model is asked to open a view.
- Answer the promise to the open message.
- In the model's method that is invoked when the dialog is accepted, promise resolveWith: theResultOfWhatHappened
- In the method that opens the tool, take the promise and use whenResolved:, whenRejected: (handle the outcome) or then:ifRejected: (handle the outcome and answer another promise -- chaining). Do not attempt to return from the handler blocks, the method will have returned by the time they are evaluated.

Of course it is a totally different style of programming than with modal windows.

Similar alternative without promises:
- Instead of a promise, store a MessageSend in the model.
- In the client of the model, store all necessary state to continue in instance variables.
- Do not expect a return value from opening the view for the model.
- Have the model send the message to self upon continuation.


Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Monticello-ct.712.mcz

Jakob Reschke
Am Do., 16. Jan. 2020 um 09:03 Uhr schrieb Thiede, Christoph <[hidden email]>:

Similar alternative without promises:

> - Instead of a promise, store a MessageSend in the model.
> - In the client of the model, store all necessary state to continue in instance variables.
> - Do not expect a return value from opening the view for the model.
> - Have the model send the message to self upon continuation.

And I would append observer pattern to this list, which is just a special case of your last point.

These proposals actually appear a bit easier and more convenient.

Actually it was just a single proposal consisting of multiple steps. :-)
 
I would like to use the same solution for DialogWindows as well which suffer from the same problem. But #inform: should be called as usual and hide any concurrent logic from the caller. How would you identify the object to resume after the information, if you called, for example, nil inform: #foo? I think the only way, without creating an extra DialogWindowModel, would be #spawnNewProcessDuring: ...

To simulate the synchronous interface, you must not only notify the client, but also block the calling process until the dialog is closed.
Quick ideas:
- I have never used it, but I heard Seaside implemented proper continuations. Maybe take a look there.
- Hacking about thisContext sender or/and suspending the process and resuming it later.
- Spawn a new UI process, once the dialog is closed have it signal a semaphore and terminate itself. Let the original UI process wait on that semaphore. Breaks other things which don't support such changes of the UI process, such as the Monticello tools. May have edge cases that leave the world without an unblocked UI process, I don't know.