tmedwards / sugarcube-2

SugarCube is a free (gratis and libre) story format for Twine/Twee.
https://www.motoslave.net/sugarcube/2/
BSD 2-Clause "Simplified" License
177 stars 41 forks source link

Please enhancd docs to include example Setting ChangeHandler #298

Closed selden closed 4 months ago

selden commented 5 months ago

Is your feature request related to a problem? Yes: results of changed settings weren't visible until the browser was refreshed.

Describe the solution you'd like. Please enhance the documentation to provide an example ChangeHandler for use with the Setting API, perhaps using location.reload() in the example.

Background:

I discovered that when I used the Setting API to change setting values, the changes made no observable difference until the browser window was refreshed. It's an annoyance for the user to have to manually refresh the browser each time a setting is changed. After some research, I discovered that HTML provides the refresh function location.reload(). Now all of my setting definitions end with the statement

    onChange : settingChangeHandler

and settingChangeHandler is defined as

   var settingChangeHandler = function () {
        location.reload();
   };

Presumably location.reload() could be specified directly in the onChange statement, but I wanted to allow for adding other directives at a later time.

greyelf commented 5 months ago

@selden

the changes made no observable difference until the browser window was refreshed.

Could you please supply an example of one of your project's Settings that is meant to update the visible content of the page, that doesn't until after web-browser page refresh is performed.

I ask because the "widescreen" related Setting.addToggle() example and the "theme" related Setting.addList() example both cause the page to visible change without the need of a refresh.

selden commented 5 months ago

They are the kinds of settings which change what is rendered on-screen.

One example is "Point of View". It sets a variable to one of "Single", "Multiple" or "Omnicient". When "Single" is selected, the reader follows the adventures of the protagonist without seeing the thoughts of the characters the protagonist is interacting with. "Multiple" reveals what they're thinking, while "Omnicient" reveals events which are taking place elsewhere but will affect the storyline.

In the body of various passages are <<if ...>>statements which reveal the appropriate text.

Here's an example I made up just now. It might have some typos, but it's based on a story I'm currently writing. Hmm. I seem to have been misspelling "Omniscient" in everything. sigh

:: my settings [script]

/* function to refresh window if a value changes
   so user doesn't have to
   */
   var settingChangeHandler = function () {
        location.reload();
   };

Setting.addHeader("Story enhancements:", "These control content visibility.");

// Setting up a basic list control for the settings property 'POV'
Setting.addList("POV", {
    label   : "Point of view:",
    list    : ["Single", "Multiple", "Omnicient"],
    default : "Single",
    onChange : settingChangeHandler
});

:: PassageHeader [nobr]

<<set $POV=settings.POV>>

:: Party?

The Protagonist asks "Where are you going?"

<<if $POV is "Multiple" or $POV is "Omnicient">>
NPC1 wonders "Why's he asking me that? Is something going on at home?"
<</if>>

<<if $POV is "Omnicient">>
Back at home NPC2 trips and bumps into the endtable. Its lamp falls and shatters on the floor.

NPC2 groans. "Now she's really gonna be mad. Surprise birthdays are supposed to be happy!"
<</if>>

Looking at him suspiciously, NPC1 replies "I'm on my way to get groceries."

If the browser window isn't refreshed, the additional blocks of text aren't immediately rendered when the Setting popup exits. Is there another way to structure this so they are?

I have a bunch of similar settings. Whether links to glossary entries and footnotes are shown is another example.

HiEv commented 5 months ago

@selden , the problem with what you're asking for is that it would would usually be unnecessary for most games, and for some games doing that would would break or disrupt gameplay.

That's why, if you want that to happen, you'll need to code it to do that yourself.

The simplest method to do that yourself would be to simply create your own macro which would let you create single, multiple, and/or omniscient chunks of text, and have the macro mark each chunk with a different CSS class or classes. Then you'd set the CSS up so that each of those chunks of text would be hidden by default. Finally, you'd let the POV setting determine which of those three classes would override the default to make elements with that class be visible, and make it so that it would update that CSS whenever the setting changed.

That way you'd have it so that changing the setting would instantly update the display. Just be careful, though, since all three chunks will still execute any code within them, even though they're not visible, so don't put any redundant code in the different versions of the passage text.

Hope that helps!

selden commented 5 months ago

Thanks! I’ll give that a try.

Selden


From: HiEv @.> Sent: Thursday, May 2, 2024 10:14:03 PM To: tmedwards/sugarcube-2 @.> Cc: Selden E. Ball, Jr @.>; Mention @.> Subject: Re: [tmedwards/sugarcube-2] Please enhancd docs to include example Setting ChangeHandler (Issue #298)

@seldenhttps://github.com/selden , the problem with what you're asking for is that it would would usually be unnecessary for most games, and for some games doing that would would break or disrupt gameplay.

That's why, if you want that to happen, you'll need to code it to do that yourself.

The simplest method to do that yourself would be to simply create your own macrohttps://www.motoslave.net/sugarcube/2/docs/#macro-api, that would let you create single, multiple, and/or omniscient chunks of text, and have the macro mark each chunk with a different CSS class or classeshttps://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class. Then you'd set the CSS up so that each of those chunks of text would be hidden by default. Finally, you'd let the POV setting determine which of those three classes would be visible, and make it so that it would update that CSS when the setting changed.

That way you'd have it so that changing the setting would instantly update the display. Just be careful, though, since all three chunks will still execute any code within them, even though they're not visible, so don't put any redundant code in the different versions of the passage text.

Hope that helps!

— Reply to this email directly, view it on GitHubhttps://github.com/tmedwards/sugarcube-2/issues/298#issuecomment-2092025528, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AACWNDDHXJSDRTGUPE26KLTZALXGXAVCNFSM6AAAAABHEMAQESVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJSGAZDKNJSHA. You are receiving this because you were mentioned.Message ID: @.***>

greyelf commented 4 months ago

@selden

They are the kinds of settings which change what is rendered on-screen.

SugarCube already contains all the features required to achieve the outcome you want, as the following Twee Notation example demonstrates...

:: StoryScript [script]
Setting.addHeader("Story enhancements:", "These control content visibility.");

var settingPOVHandler = function () {
    /* Reshow the current Passage, without updating Progress History. */
    Engine.show();
};

Setting.addList("POV", {
    label: "Point of view:",
    list: ["Single", "Multiple", "Omniscient"],
    default: "Single",
    onInit: settingPOVHandler,
    onChange: settingPOVHandler
});

:: Start
The Protagonist asks "Where are you going?"

<<if settings.POV is "Multiple" or settings.POV is "Omniscient">>
NPC1 wonders "Why's he asking me that? Is something going on at home?"
<</if>>

<<if settings.POV is "Omniscient">>
Back at home NPC2 trips and bumps into the endtable. Its lamp falls and shatters on the floor.

NPC2 groans. "Now she's really gonna be mad. Surprise birthdays are supposed to be happy!"
<</if>>

Looking at him suspiciously, NPC1 replies "I'm on my way to get groceries."

note: I strongly suggest you look up the documentation of any feature used in the above that you're not already familiar with.

WARNING: You are currently using a $POW Story variable in your Passage example to control the flow of content...

<<if $POV is "Multiple" or $POV is "Omniscient">>
NPC1 wonders "Why's he asking me that? Is something going on at home?"
<</if>>

...instead of using the settings.POV variable that is automatically generated by the Settings system...

<<if settings.POV is "Multiple" or settings.POV is "Omniscient">>
NPC1 wonders "Why's he asking me that? Is something going on at home?"
<</if>>

And there is a potential major synchronisation issue with using a Story variable over the settings variable, and that is that the Story variable's value is stored in Progress History, but the settings variable value isn't. Which means that anything the cause that History to wind back, like the Undo button being selected, or a Save being loaded, can potentially cause the POV setting to have a different value than the POV Story variable.

However, if you really want a Save to be able to reset the POV setting back to whatever state it was when the same was created then you can use a combination of onSave and onLoad event handlers to achieve that behaviour.

selden commented 4 months ago

Thanks! I'll be giving this a try.

s.

selden commented 4 months ago

@greyelf

Your suggestions helped a lot to streamline the procedures I'm currently using. Using Engine.show() especially helped.

@HiEv

I've been trying to get the Class settings to work, but my current lack of working knowledge of JS is getting in the way. I'll keep working on it though.

Thanks to you both!

@tmedwards

While I do think it'd be nice if an example of the use of ChangeHandler were included in the docs, I'll go ahead and close this request.