Millie helped me track down the source of your reported crash. A fix is included in Decker 1.60.
Internet Janitor
Creator of
Recent community posts
Well, I'm afraid text entry in Web-Decker is a little bit of a mixed bag; it depends on the combination of your operating system, web browser, and your active keyboard layout.
On MacOS, for example, the US-English keyboard layout allows characters like "ñ" to be typed with a "dead-key" combination: option+n to latch in eñe-mode, and then separately "n" to complete the sequence. Problem is, web browsers don't emit any key events for the dead key: Web-Decker only sees "n". If you're using a keyboard layout where ñ can be typed directly- without dead-keys- it will probably work, but it also may not?
On touch devices, Web-Decker will show a touch keyboard, and all the non-ASCII DeckRoman characters can be found under the "alt" key on that keyboard, using a layout loosely adapted from classic MacOS. In unlocked decks, Decker also allows a user to manually summon this keyboard with Edit -> Keycaps... from the menu. The intention is to help people work around limitations of their local keyboard layouts when they need an unusual character, but it only helps for authoring; the menu is hidden in a locked deck.
Copying and pasting text in from another application should always work, as should opening and viewing decks that already contain DeckRoman characters.
Depending on what you're trying to do, some of these workarounds may help, I hope?
I would recommend reading and asking some questions on the decker community forum. There are many Decker users there that would be happy to help. Try to explain what you want to accomplish in as much detail as possible. It also helps to describe what you have tried previously, so we can get a clearer idea of what you're struggling with.
In addition to Dialogizer, which offers blocking modal dialogs like a more general version of alert[], WigglyKit includes an example of a full-screen contraption which behaves like a non-blocking modal dialog; might be worth a look. While alert[] is not very visually customizable, it is worth noting that you can specify the first argument as rtext, which includes the possibility of specifying fonts, colors/patterns, and supplying inline images.
Great explanation, Screwtapello!
In the general case, if you want a specific row of a table at a known index as a dictionary, I'd recommend skipping the query and instead using simple indexing. The following lines are both equivalent to your third example (assuming you just want every column with its original name and order):
piece_data: (rows me.value)[row] piece_data: me.value[row]
Currently grid settings aren't persisted anywhere, but I'll give it some thought; possibly it could be stored in deck files, or perhaps it might make sense to expose scripted control via the app interface.
Lil doesn't have a per se list literal syntax. Your example is one straightforward way of making nested lists:
(list 0,1),(list 2,3)
Another fairly general way of declaring static data is to use "parse" on a string of JSON/LOVE:
"%J" parse "[[1,2,3],[4,5],[6,7,8,9]]" # ((1,2,3),(4,5),(6,7,8,9))
Within Decker it's often a good idea to store lengthy static data in a Field widget (where it can be encoded/decoded to/from LOVE via the .data attribute), or as a table stored in a Grid widget, where it can be viewed and directly manipulated. Modules (Lil libraries for Decker) don't have direct access to a deck, so they have a dedicated mechanism called a KeyStore.
If the sublists are of equal size, you could turn a flat list into a nested one using a primitive like "window":
2 window range 6 # ((0,1),(2,3),(4,5))
You could also construct a nested list piecemeal by index in a named variable:
a[0]:1,2,3 a[1]:4,5 a # ((1,2,3),(4,5))
This last approach is particularly handy for nested dictionaries; see the code examples in the reference manual entry for the Array Interface.
Does any of that help?
Decker 1.58 revises the behavior of the text-span styling commands in the Text menu; It's now much easier and less confusing to create links which use special fonts.
Decker doesn't have any per se minimum requirements; on older and slower computers it will run worse, but I'm always trying to make it usable on a wider range of devices. Some time ago I made changes to allow it to be compiled against SDL 1.2, which means that in principle it could be built to work on windows 98. RAM consumption will vary based on the size of the loaded deck and how much undo history you've accumulated; perhaps somewhere between tens of megabytes and a few hundred?
I did most of the early development for Decker in a not particularly beefy 9+ year old macbook air; Web-Decker should be usable on most inexpensive Android tablets or Chromebooks. Two years ago I posted about Decker being usable (if a little bit pokey) on the OLPC XO-4.
As a general rule, web applications are not allowed to start playing sound without user interaction. Web-Decker attempts to start its audio subsystem when the user first clicks or types on the keyboard. It's possible there's some additional inconsistency in behavior, but what you're describing sounds to me like normal webapp sandboxing constraints.
Code you add to the deck-level script is run every time any event is processed. Likewise, code in a card or widget script executes whenever an event is sent to that card (or a widget on the card), or an event is sent to that widget, respectively.
If the top-level script is just defining functions (aka event handlers) and constants this is fine and normal. If there's lots of code it can start to chew up a lot of the per-frame execution quota; this is when you might consider moving some scripts into different cards or modules.
There are some situations where more elaborate "bare" code outside event handlers is useful, but Millie is correct: most of the time, most of your code ought to go inside event handlers so that it only has a chance to start doing things when a specific event occurs.
Supposing you have several sliders on a card:
The easiest way to find the slider with the highest value would be to write a query which sorts the sliders by value (descending) and then extract their names:
extract value..name orderby value..value desc from likesDogs,likesCats,likesFish,likesChickens
In the above example, this would yield a list of widget names like so:
("likesChickens","likesDogs","likesFish","likesCats")
The "first" of such a list would be the highest-ranked (with ties naturally broken in order of appearance).
The Jam just wrapped up, with 25 fantastic entries; consider taking some time to leave nice ratings and constructive feedback on your fellow deckbuilders' creations! Many of them have included unlocked decks for you to pore over and learn a trick or two...
If anyone is nearly finished but didn't quite make the deadline, feel free to reach out to me over the next few days and I can prepare a late submission link.
Hope to see you all in December for the next Deck-Month!
Every primitive widget type can be assigned a font, as by "Widgets -> Font...".
The default font of a field widget is the builtin font "body", unless the field widget is in "code" mode, in which case it will default to the builtin font "mono".
When spans within rtext specify no font (or a nonexistent font), the font of the field widget "shows through".
You can construct links in rtext with any font via scripting. For example:
field1.value:rtext.make["before "],rtext.make["text" "menu" "mylink"],rtext.make[" after"]
The rtext table thus constructed:
The current behavior of the "Text -> Link..." menu item creates spans which do not specify a font, and thus manually-created links always use the font of the field widget. I'll make a note to revisit this.
This contraption wasn't designed with that in mind, but it's an easy modification. The script for the "resize" button in the prototype:
on click do card.event["resize"] end
Needs to call the prototype-level view[] event handler to reposition/resize the window title after user code has had a chance to run:
on click do card.event["resize"] view[] end
The "close" button requires a similar tweak.
Here's a paste-able version with both modifications:
%%WGT0{"w":[{"name":"macFieldWindow1","type":"contraption","size":[109,105],"pos":[235,38],"script":"on close do\n \nend\n\non resize do\n me.size:320,240\nend","def":"macFieldWindow","widgets":{"close":{},"resize":{"pos":[88,4]},"title":{"value":"Hello"},"drag_resize":{"pos":[93,89]},"field":{"size":[108,71],"value":{"text":["I am a draggable and resizeable window sporting a rich text field!\n","Try me!"],"font":["","menu"],"arg":["",""]}},"bar":{"size":[109,19]}}}],"d":{"macFieldWindow":{"name":"macFieldWindow","size":[59,71],"resizable":1,"margin":[22,34,33,33],"description":"a window frame in the style of MacOS 6, sporting a field as well as supporting dragging and resizing! The internal field is available at x.field","version":0.1,"script":"on get_title do title.text end\non set_title x do title.text:x end\non get_field do field end\non get_value do field.value end\non set_value x do field.value: x end\n\non view do\n field.locked:card.locked\n t:get_title[]\n if count t\n title.font:\"menu\"\n s:first title.font.textsize[t]\n title.size:(s+10),title.size[1]\n title.pos :(.5*card.size[0]-title.size[0]),title.pos[1]\n title.show:\"solid\"\n else\n title.show:\"none\"\n end\nend","template":"on close do\n \nend\n\non resize do\n \nend","image":"%%IMG2ADsARwE8IDgBAyA4AQMgOAEDIAEBBiABAQsgAQEQIAEBCyABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQcgAwEBIAEBBiABAQMgCAEBIAkBASASAQEgCQEBIAgBAyABAQYgAQEBIAkBASABARAgAQEBIAkBASABAQYgAQEDIAgBASAJAQEgEgEBIAkBASAIAQMgAQEGIAEBCyABARAgAQELIAEBBiABAQMgOAEDIDgBAyA4AT4AKQEBIA4BAwApAQEgBgEBIAcBAwApAQEgBQEBIAEBASAGAQMAKQEBIAQBASADAQEgBQEDACkBASADAQEgBQEBIAQBAwApAQEgAgEBIAcBASADAQMAKQEBIAEBASAJAQEgAgEDACkBBSAFAQQgAQEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQcgBAEDACkBASAOAQMAKQEBIA4BAwApARIAKQEBIA4BAwApAQEgDgEDACkBASAOAQMAKQEBIA4BAwApAQEgDgEDACkBEgApAQEgDgEDACkBASAOAQMAKQEBIAMBByAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEFIAUBBCABAQMAKQEBIAEBASAJAQEgAgEDACkBASACAQEgBwEBIAMBAwApAQEgAwEBIAUBASAEAQMAKQEBIAQBASADAQEgBQEDACkBASAFAQEgAQEBIAYBAwApAQEgBgEBIAcBAwApAQEgDgE+IAcBASAGAQEgCwEBIAYBASAHAQEgDgEDIAYBAiAGAQEgCwEBIAYBAiAGAQEgDgEDIAUBASABAQEgBgEBIAsBASAGAQEgAQEBIAUBASACAQcgBQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASACAQEgBQEBIAUBAyADAQEgBwEBIAIBASALAQEgAgEBIAcBASADAQEgAgEBIAUBBSABAQMgAgEBIAgBASACAQEgCwEBIAIBASAIAQEgAgEBIAIBASAFAQEgAwEBIAEBAyABAQEgCQEBIAIBASALAQEgAgEBIAkBASABAQEgAgEBIAUBASADAQEgAQEDIAIBASAIAQEgAgEBIAsBASACAQEgCAEBIAIBASACAQEgBQEBIAMBASABAQMgAwEBIAcBASACAQEgCwEBIAIBASAHAQEgAwEBIAIBByADAQEgAQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASAEAQEgBwEBIAEBAyAFAQEgAQEBIAYBASALAQEgBgEBIAEBASAFAQEgBAEBIAcBASABAQMgBgECIAYBASALAQEgBgECIAYBASAEAQEgBwEBIAEBAyAHAQEgBgEBIAsBASAGAQEgBwEBIAQBCSABAQMgDgEBIAsBASAOAQEgDgE9AAEBOg==","attributes":{"name":["title","value"],"label":["Title","Text"],"type":["string","rich"]},"widgets":{"close":{"type":"button","size":[11,11],"pos":[9,4],"script":"on click do\n card.event[\"close\"]\n view[]\nend","style":"invisible"},"resize":{"type":"button","size":[11,11],"pos":[38,4],"script":"on click do\n card.event[\"resize\"]\n view[]\nend","style":"invisible"},"title":{"type":"field","size":[8,17],"pos":[-12,1],"locked":1,"font":"menu","show":"none","border":0,"style":"plain","align":"center"},"drag_resize":{"type":"canvas","size":[14,14],"pos":[43,55],"script":"on click pos do\n field.locked:1\nend\n\non drag pos do\n card.size:(me.offset-card.pos-16)|(title.size[0]+57),85\n view[]\n field.locked:1\nend\n\non release pos do\n field.locked:card.locked\nend","show":"transparent","border":0,"draggable":1,"scale":1},"field":{"type":"field","size":[58,37],"pos":[0,18],"border":1,"scrollbar":1},"bar":{"type":"canvas","size":[59,19],"pos":[0,0],"script":"on click pos do\n field.locked:1\nend\n\non drag pos do\n card.pos:me.offset\nend\n\non release pos do\n field.locked:card.locked\nend","show":"transparent","border":0,"draggable":1,"scale":1}}}}}
Depending on what you're trying to do, either a combination of ! and = (for a scalar boolean value, logical XOR is the same as "not equal to") or possibly bits.xor[].
I encourage Linux or BSD users to build Decker from source. (It's pretty easy on MacOS, too!)
Depending on your distro, there may even be a Decker package available for your package manager.
You could mildly simplify your "on change" logic by using widget.toggle:
on change label active do if label = "Words" field1.toggle["solid" active] elseif label = "Picture" canvas1.toggle["solid" active] end end
If you're frequently changing the set of widgets affected, it might also be worth considering a data-driven approach: form a dictionary mapping labels to lists of widgets, and then toggle entire lists:
on change label active do wids.Words: field1,field2 wids.Picture: canvas1,canvas2 wids[label]..toggle["solid" active] end
And in principle, with a somewhat different contract, you could even lift the toggling up to the tabstrip itself, just leaving the user code to construct such a dictionary. This would, of course, be somewhat less flexible.
If you want to use a module from a newly-created deck you will need to import the module into your deck, just as you would to use a font or sound.
I'd just like to say that I really appreciate you leaving your original comments and following up when you're able to resolve problems on your own; it provides very valuable information for other users who are reading along.
If a widget defines no event handler for an event, the event "bubbles up" to the container card or contraption, and in the case of a card it can further "bubble up" to the deck-level script. Events which "bubble" still further are handled by default event handlers, if any exist. I take advantage of this in many of my contraptions because there is often only a single widget that can produce e.g. a "drag" or "change" event and putting all the logic in the contraption-level script where i can see it at once is slightly more convenient.
An existent but empty event hander "swallows" the event and does not give higher-level definitions a chance to do their work. I recognize that event handler "templates" can be a bit of a footgun in this case, but there isn't any way Decker can automatically distinguish "as-of-yet unfilled" handlers from intentionally empty event handlers meant to suppress bubbling.
I'd also like to note that re-pasting a contraption definition into a deck where the original has been modified will not update existing contraption instances unless the new definition has a higher version number.
Does that help clarify?
It looks like you aren't using the correct syntax for "select" queries. The "end" preceding the cursor in your screenshot is where Lil expects an expression, column name, or where/orderby/by clause. If your intention is to make a table from scratch you might mean something like:
select c1:opt from ()
But if the goal is simply to turn the list "opt" into a single-column table you could instead use the "table" operator instead of a query form:
table "alpha","beta","gamma" +---------+ | value | +---------+ | "alpha" | | "beta" | | "gamma" | +---------+
Does that make sense?
Nicely done!
One idea that might simplify this sort of deck in future projects would be to take advantage of Lil's ".." notation. Whenever you have a "path" to a deck part or attribute:
Cloak.widgets.FromCloakroom.show:"none"
A pair of dots next to one another act like a wildcard, substituting in each element of a list or dictionary. For example, you could hide every widget on the card "Cloak" like so:
Cloak.widgets..show:"none"
Often it's helpful to discriminate a bit more. We can write a "helper" function that queries a card for widgets whose name matches a glob-pattern and install it in the deck-level script (File -> Properties... -> Script...):
on find_wids card namePattern do extract value where key like namePattern from card.widgets end
Then, instead of writing something like
Cloak.widgets.FromCloakroom.show:"none" Cloak.widgets.FromCloakroom1.show:"none" Cloak.widgets.FromCloakroom2.show:"none" Cloak.widgets.FromCloakroom3.show:"none"
We could write
find_wids[Cloak "FromCloakroom*"]..show:"none"
Not only is this a bit shorter, it could make modifying the deck much easier: so long as you follow a consistent naming convention, you could add or remove more widgets without needing to update these scripts.
Just for the record, you can represent counters using a field widget via its .text or .data attributes, or alternatively you could use a slider widget via its .value attribute. Sliders are slightly more convenient than fields for representing numeric values within a known range, since their .value is always a number and is automatically "clamped".
Widgets set to "Show None" behave identically to visible widgets from a scripting perspective; they "remember" their contents across events and when the deck is saved. If there are any widgets whose contents you don't want preserved, you can mark them as Volatile. Sometimes volatile widgets are useful for "resetting" the state of a game.
Does that help clarify?
To make the process more generic, you might write the animated field's script something like this:
on view do s:deck.sounds@"clip1","clip2","clip3" t:0 each snd in s if (0+me.text)~floor t play[snd] end t:t+60*snd.duration end me.text:(floor t)%me.text+1 end
Collect a list of sound clips (s), then track a total time (t) while iterating through the sounds. A new sound needs to be played every time we reach the total duration of the preceding sounds (multiplying sound.duration by 60 to convert it from seconds to frames). To loop the sequence, the overall frame counter should be incremented modulo the sum of the duration of every sound, which we already computed in "t" by the end of the loop. For best results, make all your sound clips have a duration which evenly divides into frames. Note how the indexing to compute "s" offers a convenient way to repeat "phrases" of a song within a sequence without duplicating their sound clips.
If you copy the blob of text starting with "%%WGT0" and ending in the final "}" to your operating system's clipboard, and then within Decker choose "Edit -> Paste Widgets" (or "Edit -> Paste" in web-decker), the sample contraption instance along with its prototype will be added to the current card. If you already have a prototype with an identical name and a lower "version" number, the revised prototype will upgrade any existing contraption in place.
The "%%WGT0{...}" format is literally how widgets (and their dependencies; referenced fonts and prototypes) are represented in the system clipboard by Decker.
I had a go at creating this contraption as described:


as a paste-able example:
%%WGT0{"w":[{"name":"q1","type":"contraption","size":[185,24],"pos":[100,84],"def":"questionAsker","widgets":{"questionText":{"value":"A hotdog is a sandwich?"},"bp":{},"bz":{},"bn":{},"answerVal":{"value":"-1"}}}],"d":{"questionAsker":{"name":"questionAsker","size":[185,24],"resizable":1,"margin":[3,3,71,4],"description":"Your choices will be recorded for quality-assurance purposes.","version":1,"script":"on get_question do\n questionText.text\nend\non set_question v do\n questionText.text:v\nend\non get_answer do\n answerVal.data\nend\non set_answer v do\n answerVal.data:v\n view[]\nend\non view do\n each val wid in (bp,bz,bn) dict 1,0,-1\n wid.show:(\"solid\",\"invert\")[answerVal.data~val]\n end\nend","attributes":{"name":["question","answer"],"label":["Question","Answer"],"type":["string","number"]},"widgets":{"questionText":{"type":"field","size":[115,20],"pos":[2,2],"locked":1,"border":0,"value":"Question?"},"bp":{"type":"button","size":[19,20],"pos":[120,2],"script":"on click do\n answerVal.data:1\n view[]\nend","text":"+","style":"rect"},"bz":{"type":"button","size":[19,20],"pos":[142,2],"script":"on click do\n answerVal.data:0\n view[]\nend","show":"invert","text":"0","style":"rect"},"bn":{"type":"button","size":[19,20],"pos":[164,2],"script":"on click do\n answerVal.data:-1\n view[]\nend","text":"-","style":"rect"},"answerVal":{"type":"field","size":[63,20],"pos":[120,-38],"show":"none","style":"plain","value":"0"}}}}}
Millie's on the right track with respect to using "me.parent.widgets". The Prototype interface has a ".widgets" attribute, so this works in the design preview, but the Contraption interface does not (otherwise there'd be no "abstraction barrier" and anything could freely fiddle with the innards of contraption instances).
I use the answerVal.data attribute for reading and writing the contents of the inner field so that the .answer attribute is exposed as a number, rather than a string. I do like Millie's suggestion to use a Slider instead; fewer possibilities for using it wrong. I also modified set_answer[] to call a contraption-level view[] function that updates the highlighting status of each button; otherwise writes to this attribute won't appear visually:
on set_answer v do answerVal.data:v view[] end on view do each val wid in (bp,bz,bn) dict 1,0,-1 wid.show:("solid","invert")[answerVal.data~val] end end
The buttons themselves now just set AnswerVal.data and invoke view[]:
on click do answerVal.data:1 view[] end
Does that help clear things up?
Did you import the zazz and dd modules into your deck using the Font/DA Mover?