diff --git a/LANGUAGE.md b/LANGUAGE.md index a9421b1b..bf409864 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -371,8 +371,10 @@ program Pack { dispatching needs no state parameter. - **When the queue is drained:** at the end of every phase of the frame loop (so what the `Input` phase dispatches is reduced before `Update`); after every phase of the ludic.base system runner - (`core_tick_all`); and wherever the program calls `drain_actions()` (an `entry` program, a test, - a loop of its own). Draining runs the actions in the order they were dispatched, and each action's + (`core_tick_all`); when ludic.ui has run a frame's presses (`ui_show`, `ui_press`), so a button's + action is reduced before the host presents the frame, not a frame later; and wherever the program + calls `drain_actions()` (an `entry` program, a test, a loop of its own - and a host that runs UI + presses some other way calls it before it presents). Draining runs the actions in the order they were dispatched, and each action's reducers in the order of their states' names - never the order of imports - so the same actions make the same changes on every machine and in a replay. - **An action a reducer dispatches** is queued behind the rest and reduced in the same drain, never diff --git a/changes/ui-press-drain.md b/changes/ui-press-drain.md new file mode 100644 index 00000000..bf682862 --- /dev/null +++ b/changes/ui-press-drain.md @@ -0,0 +1,7 @@ +bump: patch +type: fix +**An action a UI button dispatches is reduced before the frame is presented.** ludic.ui runs a frame's +presses after drawing it, and an action they dispatched waited for the end of the phase - after the +host had presented - so what the button changed showed a frame late. `ui_show` and `ui_press` now +drain the queue once the presses have run. A host that runs presses some other way calls +`drain_actions()` before it presents. diff --git a/examples/actions/ui_press.ludic b/examples/actions/ui_press.ludic new file mode 100644 index 00000000..3c37e5e9 --- /dev/null +++ b/examples/actions/ui_press.ludic @@ -0,0 +1,22 @@ +# ui_press.ludic - 0.R4: an action a UI control dispatches is reduced before the UI hands control +# back, so the code after it - and the frame the host presents - already sees the change +import "ludic.ui" +program UiPress { + numbers float + state Score { points: int = 0 } + action Scored { n: int = 1 } + reducer Score on Scored(s: mut Score, a: Scored) { s.points += a.n } + import "ui_press_parts/Adder.ludic" + function frame(ui_st: mut UiState) -> UiNode { + let root: UiNode = ui_nodes(ui_st, "Adder", null) + ui_place(ui_st, root, 0.0, 0.0, 300.0, 200.0) + return root + } + entry (ui_st: mut UiState, score: Score) { + let root = frame(ui_st) + ui_press(ui_st, root.children[0].children[0]) + let after_one = score.points + ui_press(ui_st, root.children[0].children[0]) + print(`{after_one} {score.points}`) + } +} diff --git a/examples/actions/ui_press_parts/Adder.ludic b/examples/actions/ui_press_parts/Adder.ludic new file mode 100644 index 00000000..d3a5c8f9 --- /dev/null +++ b/examples/actions/ui_press_parts/Adder.ludic @@ -0,0 +1,4 @@ +# Adder.ludic - a button that says what happened and changes nothing itself +component Adder { + on add(n: int) { dispatch Scored { n: n } } +} diff --git a/examples/actions/ui_press_parts/Adder.xml b/examples/actions/ui_press_parts/Adder.xml new file mode 100644 index 00000000..2b736719 --- /dev/null +++ b/examples/actions/ui_press_parts/Adder.xml @@ -0,0 +1,3 @@ +