Commit 6bd817e2 authored by Leon Arnott's avatar Leon Arnott
Browse files

Renamed (reload:) and (icon-reload:) to (restart:) and (icon-restart:),...

Renamed (reload:) and (icon-reload:) to (restart:) and (icon-restart:), keeping (reload:) as an alias.

Also:
 * Fixed some minor things in The Harlowe Toolbar, as well as added a minimise button to the whole thing.
 * Columns and aligners no longer have title text in Debug Mode, due to it being kind of confusing.
 * Trying out the notion that <tw-transition-container>s should never get "inline-block" at all, instead just "inline" or "block". I checked the original commit for this (a38a93a77b97), and I don't believe it was ultimately correct.
 * Fixed another Chrome opacity bug with transitions.
 * Fixed a CSS bug with <tw-dialog-links>, caused by Chrome not supporting justify-content:end.
 * Added a TwineScript_is() method to basic commands, allowing them to be considered equal by `is` in most situations where that matters.
parent 5fea4b2ff030
......@@ -55,6 +55,7 @@ Documentation is at http://twine2.neocities.org/. See below for compilation inst
* The behaviour for multiple `(click:)` macros affecting the same hook (such as in `|A>[B] (click: ?A)[1] (click: ?A)[2]`) has changed to be slightly more intuitive: formerly, as you clicked the hook, the last `(click:)` would activate first (so, `[2]` then `[1]`). Now, they activate from first to last. This also applies to `(mouseover:)` and `(mouseout:)`.
* Now, the text input box in `(prompt:)` is wider, and is auto-focused when the dialog appears, allowing the player to type into it without having to click it.
* `(hook:)` now gives an error if it's given an empty string.
* Commands are now considered equal by `is` and other operators if they have identical data values given to them. So, for instance, `(print: "34") is (print:"34")` will now produce `true`.
* Now, `contains`, `is in` and `is not in` will emit an error if it's used to check if a string contains a non-string value, such as a number or boolean. This brings it into consistency with a few other operations, like trying to set a string's `1st` to something other than a single character.
* Slightly altered the behaviour of `matches` when both sides are datatypes - now, identical datatypes will match each other, instead of only matching if one side is `datatype`.
* The `(str-repeated:)` and `(repeated:)` macros now no longer error if the given number of repetitions is 0. (They will return empty strings and arrays, respectively.)
......@@ -78,6 +79,7 @@ Documentation is at http://twine2.neocities.org/. See below for compilation inst
* `(background:)` can now be shortened to `(bg:)`.
* `(text-rotate:)` is now aliased to `(text-rotate-z:)`, for consistency with the new macros below.
* `(alert:)` has been renamed to `(dialog:)`, retaining the original name `(alert:)` as an alias for it. Moreover, it can now accept any amount of link text, as well as a bound variable to set to the text of whichever link you click. Also, changers like `(t8n:)` can now be attached to the `(dialog:)` macro.
* `(reload:)` has been renamed to `(restart:)`, retaining the original name as an alias for it.
* `(rgb:)` now accepts fractional values for the R, G, and B components.
* Made the syntax highlighting dark mode colours 50% brighter.
* Markup that's inside the verbatim markup will no longer be syntax-highlighted as if it was outside it.
......@@ -211,7 +213,7 @@ Documentation is at http://twine2.neocities.org/. See below for compilation inst
* Added `(link-fullscreen:)`, a command macro that creates a link that, when clicked, toggles the browser's fullscreen mode on and off. It takes two link text options, one for entering fullscreen, and one for exiting it, which are automatically updated to match the browser's current fullscreen state. Note that this will only make the `<html>` element fullscreen, for numerous reasons (mainly that enchanting the `<tw-story>` element with `(enchant:?page,` will deactivate fullscreen if it's the fullscreen element, due to the macro call wrapping `<tw-story>` in another element). Also note that because most browsers allow users to exit fullscreen mode at any time, there's no way to force fullscreen mode into one state or another. Also note that, because browsers only allow fullscreen mode to be entered from interaction elements, like links or buttons, there can't be a low-level "toggle fullscreen" command macro as an alternative to this.
* Added `(link-storylet:)`, a command macro that creates a link to the nth open storylet (or, if a 'where' lambda is given, the first open storylet whose (passage:) datamap matches the lambda).
* Added `(icon-undo:)` and `(icon-redo:)`, two commands that replicate the default sidebar's "undo" and "redo" icons. Use these to reinsert these into the sidebar if you've modified it using `(replace:)` or `(append:)`, or simply place those icons into your passage text if you wish. You can change the icon to use a different Unicode character if you wish. You can also add text labels to these icons, which appear below them.
* Added `(icon-reload:)` and `(icon-fullscreen:)`, two additional commands that can be used, with `(prepend:)` and `(append:)`, to add "fullscreen" and "reload" buttons to the sidebar. The "fullscreen" icon works similarly to the `(link-fullscreen:)` macro described above, while the "reload" icon performs similarly to `(reload:)` when clicked. These offer the same options as `(icon-undo:)` and `(icon-redo:)`.
* Added `(icon-restart:)` and `(icon-fullscreen:)`, two additional commands that can be used, with `(prepend:)` and `(append:)`, to add "fullscreen" and "reload" buttons to the sidebar. The "fullscreen" icon works similarly to the `(link-fullscreen:)` macro described above, while the "reload" icon performs similarly to `(reload:)` when clicked. These offer the same options as `(icon-undo:)` and `(icon-redo:)`.
* Added `(icon-counter:)`, which produces a numeric display element that live-updates to match a bound number variable (rounded to a whole number). The element is roughly the same size as the sidebar icons, and designed to be used in the sidebar alongside them.
* Added a debugging command, `(mock-visits:)`, which lets you re-create a game state where certain passages have been visited a certain number of times, so that the `visits` keyword and the `(history:)` array produce desired results. This command will cause an error unless it's used in debug mode.
* Added a debugging command, `(assert:)`, which can be used to write an "assertion" expression about the game state of your story that should always be `true`, such as `(assert: $HP <= $maxHP)`. If some bug causes the assertion to produce `false`, a helpful error will be produced.
......
......@@ -595,7 +595,7 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
passage and forgetting any variable changes that occurred in this passage.
Example usage:
`You scurry back whence you came... (live:2s)[(undo:)]` will undo the current turn after 2 seconds.
`You scurry back whence you came... (after:2s)[(undo:)]` will undo the current turn after 2 seconds.
Rationale:
The (go-to:) macro sends players to different passages instantly. But, it's common to want to
......@@ -832,15 +832,15 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
#sidebar 4
*/
/*d:
(icon-reload: [String]) -> Command
(icon-restart: [String]) -> Command
Creates an icon, similar to those in the sidebar, that, if visible and clicked, reloads the whole page, restarting the story from the beginning.
Example usage:
`(replace:?sidebar)[(icon-reload: )]` replaces the sidebar with just the "reload" icon.
* `(icon-reload:"⟲")` creates an element that uses &#27F2; as its icon instead of the default.
* `(icon-reload:"Restart")` creates an element with the label "Restart" under it.
* `(icon-reload:"⟲", "Restart")` combines both of the above.
`(replace:?sidebar)[(icon-restart: )]` replaces the sidebar with just the "reload" icon.
* `(icon-restart:"⟲")` creates an element that uses &#27F2; as its icon instead of the default.
* `(icon-restart:"Restart")` creates an element with the label "Restart" under it.
* `(icon-restart:"⟲", "Restart")` combines both of the above.
Rationale:
By default, each passage in a Harlowe story features a narrow sidebar to the left, housing "Undo" and "Redo" menu icons.
......@@ -2537,13 +2537,14 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
[String], false)
/*d:
(reload:) -> Command
(restart:) -> Command
Also known as: (reload:)
When this command is used, the player's browser will immediately attempt to reload
the page, in effect restarting the entire story.
Example usage:
`(click:"Restart")[(reload:)]`
`(click:"Restart")[(restart:)]`
Details:
Normally, Harlowe stories will attempt to preserve their current game state across browser page reloads.
......@@ -2555,16 +2556,16 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
This command can't have changers attached - attempting to do so will produce an error.
See also:
(icon-reload:)
(icon-restart:)
Added in: 1.0.0
#navigation
*/
("reload",
(["restart","reload"],
noop,
(/* no cd because this is attachable:false */ ) => {
if (State.pastLength < 1) {
return TwineError.create("infinite", "I mustn't (reload:) the page in the starting passage.");
return TwineError.create("infinite", "I mustn't (restart:) the story in the starting passage.");
}
if (State.hasSessionStorage) {
sessionStorage.removeItem("Saved Session");
......
......@@ -14,7 +14,7 @@ define(['utils', 'macros', 'state', 'utils/operationutils', 'datatypes/changerco
(set:$ghostlyLaughter to (macro: num-type _o, [
(output: )+(text-rotate:(random:0,360))+(text-colour:(hsla:0, 1, 0.5, _o))[HE HE HE]
]))
($ghostlyLaughter:) ($ghostlyLaughter:) ($ghostlyLaughter:)
($ghostlyLaughter:0.9) ($ghostlyLaughter:0.5) ($ghostlyLaughter:0.3)
```
The following custom macro creates a text string based on how many turns the player has taken. It takes no data.
......
......@@ -464,7 +464,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
in a hook (such as `(event:when $a is 1)[(link-goto:"?")]`).
See also:
(live:)
(live:), (after:), (more:)
Added in: 3.0.0
#live 3
......
......@@ -114,7 +114,7 @@ define(['macros', 'utils', 'utils/operationutils', 'datatypes/colour', 'datatype
(num:)
Added in: 1.0.0
#string
#string 1
*/
(["str", "string", "text"],
/*
......
......@@ -291,6 +291,9 @@ define(['jquery', 'utils/naturalsort', 'utils', 'utils/operationutils', 'datatyp
TwineScript_ToSource() {
return "(" + firstName + ":" + args.map(toSource) + ")";
},
TwineScript_is(other) {
return toSource(this) === toSource(other);
},
},
/*
Only assign the TwineScript_Attach() method, and a TwineScript_Run() that
......
......@@ -1419,16 +1419,18 @@
{
type: 'radios',
name: 'Affect:',
options: ["The attached hook", "The remainder of the passage or hook.", "The entire page."],
options: ["The attached hook", "The remainder of the passage or hook.", "The entire passage.", "The entire page."],
model(m, el) {
const v = el[$]('input:checked').value;
const v = el[$]('input:checked');
const index = Array.from(el[$$]('label')).indexOf(v.parentNode);
const changers = Object.entries(m.changers);
if (v.includes('page') && changers.length) {
m.wrapStart = "(enchant:?page," + changers.map(([k,v]) => `(${k}:${v.join(',')})`).join('+') + ")";
if (index >= 2 && changers.length) {
m.wrapStart = "(enchant:?pa" + (index === 2 ? "ssa" : "") + "ge," + changers.map(([k,v]) => `(${k}:${v.join(',')})`).join('+') + ")";
m.wrapEnd = "";
m.suppressedChangers = m.changers;
m.changers = {};
} else if (v.includes('passage')) {
} else if (index === 1) {
m.wrapStart = "[=\n";
m.wrapEnd = "";
}
......@@ -1770,8 +1772,8 @@
ifArgs[0] += elem[$]('input').value;
if (ifArgs[0].includes(' % ')) {
ifArgs[0] += " is 0";
// Replace "% 2 is 0" with "is even"
ifArgs[0] = ifArgs[0].replace(" % 2 is 0", " is even");
// Replace "% 2 is 0" with "is an even"
ifArgs[0] = ifArgs[0].replace(" % 2 is 0", " is an even");
}
m.valid = true;
},
......@@ -1805,7 +1807,7 @@
min: 1,
max: 999,
model(m, elem) {
m.changerNamed('event').push(`when time >= ${elem[$]('input').value}s`);
m.changerNamed('after').push(`${elem[$]('input').value}s`);
m.valid = true;
},
},
......@@ -2650,6 +2652,16 @@
html: fontIcon('question'),
onClick: () => window.open(`https://twine2.neocities.org/`, "Harlowe Documentation", 'noopener,noreferrer')
},
(() => {
const button = el('<button style="position:absolute;right:1em;margin-top:-2em">' + fontIcon('chevron-up') + "</button>");
button.addEventListener('click', () => {
toolbarElem.classList.toggle('harlowe-3-minimised');
const list = button.firstChild.classList;
list.toggle('fa-chevron-down');
list.toggle('fa-chevron-up');
});
return button;
})(),
],
}
),
......
......@@ -68,7 +68,8 @@
string: `This is <b>string data</b>. Strings are sequences of text data enclosed in matching " or ' marks. Use a \`\\\` inside a string to "escape" the next character. Escaped " or ' marks don't count as the end of the string.`,
hookName: ({name}) => `This <b>hook name</b> refers to all hooks named "\`${name}\`" in this passage.`,
cssTime: ({value}) => `This is <b>number data</b> in CSS time format. Harlowe automatically converts this to a number of milliseconds, so this is identical to ${value}.`,
datatype: `This is the name of a <b>datatype</b>. Use these names to `,
datatype: `This is the name of a <b>datatype</b>. Use these names to check what kind of data a data value is, using the \`matches\` or \`is a\` operators.<br>`
+ `You can also use them in a \`(set:)\` or \`(put:)\` to restrict that variable's data for error-checking purposes. (See the documentation for details.)`,
colour: ({text}) => `This is a ` + (text.startsWith('#')
? "<b>HTML colour value</b>. Harlowe can use this as colour data."
: `built-in Harlowe <b>colour value</b>. The built-in colours are \`red\`, \`orange\`, \`yellow\`, \`lime\`, \`green\`, \`cyan\` (alias \`aqua\`), \`blue\`, \`navy\`, \`purple\`, \`fuchsia\` (alias \`magenta\`), \`white\`, \`gray\` (alias \`grey\`), \`black\`, and \`transparent\`.`),
......
......@@ -272,7 +272,6 @@ define(['utils', 'markup', 'twinescript/compiler', 'internaltypes/twineerror'],
}
out += '<tw-align ' + (style ? ('style="' + style + '"') : '')
+ (Renderer.options.debug ? ' title="' + token.text + '"' : "")
+ '>' + body + '</tw-align>\n';
token = tokens[i];
}
......@@ -323,9 +322,7 @@ define(['utils', 'markup', 'twinescript/compiler', 'internaltypes/twineerror'],
out += "<tw-columns>"
+ columns.map(e =>
`<tw-column type=${e.type} ${''
} style="width:${e.width/totalWidth*100}%; margin-left: ${e.marginLeft}em; margin-right: ${e.marginRight}em;" ${
(Renderer.options.debug ? ` title="${e.text}"` : "")
}>${e.body}</tw-column>\n`
} style="width:${e.width/totalWidth*100}%; margin-left: ${e.marginLeft}em; margin-right: ${e.marginRight}em;">${e.body}</tw-column>\n`
).join('')
+ "</tw-columns>";
}
......@@ -561,7 +558,6 @@ define(['utils', 'markup', 'twinescript/compiler', 'internaltypes/twineerror'],
out += '<tw-expression type="' + token.type + '" name="' + escape(token.name || token.text) + '"'
// Debug mode: show the macro name as a title.
//TODO: enable this for all modes, and have Section remove it
+ (Renderer.options.debug ? ' title="' + escape(token.text) + '"' : '')
+ (blockers.length ? ' blockers="' + escape(JSON.stringify(compiledBlockers)) + '"' : '')
+ ' js="' + escape(Compiler(token)) + '"'
......
......@@ -532,10 +532,7 @@ define(['jquery', 'markup', 'utils/polyfills'],
being part of usuallyBlockElements.
*/
if (Utils.childrenProbablyInline(el)) {
/*
If there are no element children of the container (only text), simply use 'inline'.
*/
el.css('display','inline' + (el.children().length ? '-block' : ''));
el.css('display','inline');
}
/*
Special case: due to <tw-backdrop> and <tw-story> having display:flex, getComputedStyle() will always report 'display:block' for
......@@ -545,11 +542,9 @@ define(['jquery', 'markup', 'utils/polyfills'],
/*
Both of these are necessary for overriding "display:inline-block" for certain transitions,
including those that use transform.
setAttribute() must be used to add "!important".
*/
el.css({
display:'block !important',
width:"100%",
});
el[0].setAttribute('style', el[0].getAttribute('style') + "display:block !important;width:100%");
}
});
/*
......@@ -606,10 +601,7 @@ define(['jquery', 'markup', 'utils/polyfills'],
*/
requestAnimationFrame(() => {
if (Utils.childrenProbablyInline(el)) {
/*
If there are no element children of the container (only text), simply use 'inline'.
*/
el.css('display','inline' + (el.children().length ? '-block' : ''));
el.css('display','inline');
}
/*
Special case: due to <tw-backdrop> and <tw-story> having display:flex, getComputedStyle() will always report 'display:block' for
......@@ -619,11 +611,9 @@ define(['jquery', 'markup', 'utils/polyfills'],
/*
Both of these are necessary for overriding "display:inline-block" for certain transitions,
including those that use transform.
setAttribute() must be used to add "!important".
*/
el.css({
display:'block !important',
width:"100%",
});
el[0].setAttribute('style', el[0].getAttribute('style') + "display:block !important;width:100%");
}
});
......
......@@ -130,7 +130,7 @@ const outputFile = {
"twineLink:not(.text)": typeColours.twineLink,
tag:
"tag, scriptStyleTag, comment":
"color: hsla(240,34%,25%,1.0);",
boolean: typeColours.boolean,
......@@ -141,6 +141,7 @@ const outputFile = {
hookName: typeColours.hookName,
datatype: typeColours.datatype,
colour: typeColours.colour,
cssTime: typeColours.number,
"variableOccurrence, hookOccurrence":
"background: hsla(159,50%,75%,1.0) !important;",
......
......@@ -3,6 +3,9 @@
overflow-y: auto;
margin-bottom:4px;
}
.harlowe-3-minimised .harlowe-3-toolbarPanel > :not(:last-child) {
display:none;
}
.harlowe-3-toolbarPanel code:not(:empty) {
font-size:1rem;
border: 1px dotted hsl(0, 0%, 50%);
......@@ -42,7 +45,7 @@
min-width: 28px;
}
.harlowe-3-toolbarPanel > button:not(:active):not(.active) {
.harlowe-3-toolbarPanel > button:not(:active):not(.active):not(:last-child) {
border: 1px solid hsla(0,0%,50%,0.5);
border-top: none;
border-left: none;
......
......@@ -21,8 +21,7 @@ tw-dialog {
tw-dialog-links {
text-align:right;
@include flexbox;
@include justify-content(end);
justify-content:end;
@include justify-content(flex-end);
}
tw-backdrop {
z-index: $zindex - 3;
......
......@@ -142,8 +142,8 @@ tw-link.enchantment-button,
/*
...by link changers and (click:) links being enchanted by (enchant:), or link expressions changed by attached (button:) changers...
*/
.enchantment-button:not(.enchantment-link) > tw-link,
.enchantment-button:not(.enchantment-link) > .enchantment-link {
.enchantment-button:not(.enchantment-link) tw-link,
.enchantment-button:not(.enchantment-link) .enchantment-link {
@include button-links;
}
/*
......
......@@ -451,22 +451,25 @@ describe("basic command macros", function() {
});
});
describe("the (reload:) macro", function() {
describe("the (restart:) macro", function() {
// window.location.reload cannot be spied on, as it and window.location are non-configurable
it("takes no arguments", function() {
expect("(set: $x to (reload:1))").markupToError();
expect("(set: $x to (reload:'e'))").markupToError();
expect("(set: $x to (restart:1))").markupToError();
expect("(set: $x to (restart:'e'))").markupToError();
});
it("evaluates to a command object that can't be +'d", function() {
expect("(print: (reload:) + (reload:))").markupToError();
expect("(print: (restart:) + (restart:))").markupToError();
});
it("can be (set:) into a variable", function() {
expect("(set: $x to (reload:))").not.markupToError();
expect("(set: $x to (restart:))").not.markupToError();
});
it("can't be used in the first turn", function() {
clearState();
expect("(reload:)").markupToError();
});
it("is aliased as (reload:)", function() {
expect("(print: (reload:) is (restart:))").markupToPrint('true');
});
});
describe("the (goto-url:) macro", function() {
......
......@@ -356,7 +356,7 @@ describe("patterns", function() {
});
expect("(print: (p-not:...'aeiouy'))").not.markupToError();
expect("(print: (p-not:num))").markupToError();
expect("(print: (p-not:...num))").markupToError()
expect("(print: (p-not:...num))").markupToError();
expect("(print: (p-not:str))").markupToError();
expect("(print: (p-not:(p:'a')))").markupToError();
expect("(print: (p-not:))").markupToError();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment