Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(+1)

This is a question regarding scripting. I would like to have a button "toggle" between two states; revealing a widget A, then hiding a widget A, then revealing a widget A, etc. but don't know what the script for that would look like. And speculatively, would it be possible to add other elements so that the button might cycle between revealing widget A, to revealing widget B,  to revealing widget C, then back to hiding them all? Thanks in advance!

(1 edit)

The first example is very straightforward: widgets have a .toggle[] method. If you call it with a single argument it will alternate the value of their .show attribute between "none" and the string specified ("solid", "transparent", or "invert"):

on click do
  a.toggle["solid"]
end

You can see more examples of .toggle[] in the release notes.

Cycling through displaying several different widgets is a bit more complicated.


One approach would be to test the .show attributes of widgets, find which one is currently visible, and then set the .show attributes of all the widgets in a group in order to advance to the "next" selection. The main issue here is it's very ugly and inconvenient to add more widgets to the cycle:

on click do
  if     "solid"~a.show a.show:c.show:"none" b.show:"solid"
  elseif "solid"~b.show b.show:a.show:"none" c.show:"solid"
  else                  c.show:b.show:"none" a.show:"solid"
  end
end

We can reduce repetition and make maintenance a bit easier by making a list of widgets of interest, scanning for them in a loop, and using an index modulo the length of the list to pick the next item:

on click do
  cycle:a,b,c
  current:0
  each wid index in cycle
    if "solid"~wid.show current:index end
  end
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

If we want to get really clever, there's also a "vector-oriented" way to handle that search instead of an each-loop. In general, multiplying a list of integers [0,n) by a length-n list containing zeroes and a single one will "mask out" the index of the one:

(0,1,2,3)*(1,0,0,0)   # (0,0,0,0)
(0,1,2,3)*(0,1,0,0)   # (0,1,0,0)
(0,1,2,3)*(0,0,1,0)   # (0,0,2,0)
(0,1,2,3)*(0,0,0,1)   # (0,0,0,3)

So we could compute "current" like so:


Giving a complete script as follows:

on click do
  cycle:a,b,c
  current:sum(range count cycle)*"solid"=cycle..show
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

If you want to include "don't show anything" in the cycle, you can add a "0" dummy element to the list of widgets; 0.show will always be 0, which is not equal to the string "show", and attempting to set the .show property of a number is likewise harmless:

on click do
  cycle:0,a,b,c
  current:sum(range count cycle)*"solid"=cycle..show
  cycle..show:"none"
  cycle[(count cycle)%1+current].show:"solid"
end

Does that answer the question?

Edit: one more note: depending on what you're trying to do it might be worth considering using a slider widget instead of a button; Sliders naturally represent choosing between a range of integers, which could represent an index into a list:

on change val do
  cycle:0,a,b,c
  cycle..show:"none"
  cycle[val].show:"solid"
end

See also, the enum contraption.

(+1)

This is great. Exactly what I was looking for (and more!) It's enlightening to see how many different ways their are to tackle a problem. Thanks so much for this John.