It is currently March 28th, 2024, 9:15 pm

Dynamic Menu

Discuss the use of Lua in Script measures.
jam3sn
Posts: 6
Joined: May 30th, 2014, 8:06 pm
Location: Cardiff

Dynamic Menu

Post by jam3sn »

Hi all,

I'm working on an update for a Skin I released on DeviantART that allows users to have a draw out menu with 6 menu options, however, the users have asked me to allow the amount of options to be optional, so say 12 instead of 6. I'm trying to write a script that will create the relevant amount of menu options from the user. The issue I'm running into is creating the meters.

E.g. Andy wants 3 options in his menu, however Sue wants 8 options on hers. So after stating that in the config, settings or whatever, I need to make the relevant amount of meters reflecting the options they chose. I've read LUA can't physically create new meters, just change the values, so what would you suggest? Create a maximum of say 20 options, with 20 basic meters laid out but hidden until activated by the script?

Here's the skin: http://jam3sn.deviantart.com/art/Zon3-Bar-1-0-0-465659014

Cheers.
User avatar
killall-q
Posts: 305
Joined: August 14th, 2009, 8:04 am

Re: Dynamic Menu

Post by killall-q »

It's simpler by a factor to preallocate an excess number of meters and hide the unnecessary ones, especially when there is a limit to how many meters will fit on the screen anyway.

There is no reason to use a 200 ms update rate. Proper use of !UpdateMeter/!Redraw will make skins respond instantaneously even with !Update=-1.

There is no such thing as an Align= option, only StringAlign= for string meters.

That is not the normal usage for bar meters, for solid rectangles, use image meters.

"#" does not start comments, ";" does. In-line comments are not supported. (Hmm... is this in the manual? I can't find it.)

User customizeable variables should be surrounded by quotes to avoid this situation. Your BarOption meters were also lacking brackets.

Use of meter styles could shorten your code by half.

Code: Select all

[sBarOp]
X=72
Y=R
W=168
H=47
Padding=10,15,10,0
FontFace=#Font#
FontSize=16
FontColor=255,255,255
ClipString=1
AntiAlias=1
Group=Bar
Hidden=1
MouseLeaveAction=[!SetOption #CURRENTSECTION# SolidColor ""][!UpdateMeter #CURRENTSECTION#][!Redraw]
MouseOverAction=[!SetOption #CURRENTSECTION# SolidColor 65,165,255,100][!UpdateMeter #CURRENTSECTION#][!Redraw]

[BarOp1]
Meter=STRING
MeterStyle=sBarOp
Y=72
Text=#BarOption1#
LeftMouseUpAction=["BarOption1Link"]

[BarOp2]
Meter=STRING
MeterStyle=sBarOp
Text=#BarOption2#
LeftMouseUpAction=["BarOption2Link"]
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Dynamic Menu

Post by jsmorley »

killall-q wrote: "#" does not start comments, ";" does. In-line comments are not supported. (Hmm... is this in the manual? I can't find it.)
I'm not sure it is. I'll have to look for the best place to talk about comments. You are correct that inline comments are not supported. There are some places where they will just be ignored and thus behave as if they were an inline comment, and other places where they will cause an error, as they will be interpreted as an additional parameter / value on the existing line. They are only safely and properly treated as a comment if ";" is used to start the line. Inline comments should never be used.

[MeterString]
Meter=String
Text=Some Text ;change this to the text you want.

This will in fact literally display "Some Text ;change this to the text you want.", as the ";" character is perfectly valid in a string in Rainmeter.
jam3sn
Posts: 6
Joined: May 30th, 2014, 8:06 pm
Location: Cardiff

Re: Dynamic Menu

Post by jam3sn »

jsmorley wrote: I'm not sure it is. I'll have to look for the best place to talk about comments. You are correct that inline comments are not supported. There are some places where they will just be ignored and thus behave as if they were an inline comment, and other places where they will cause an error, as they will be interpreted as an additional parameter / value on the existing line. They are only safely and properly treated as a comment if ";" is used to start the line. Inline comments should never be used.

[MeterString]
Meter=String
Text=Some Text ;change this to the text you want.

This will in fact literally display "Some Text ;change this to the text you want.", as the ";" character is perfectly valid in a string in Rainmeter.
Right OK, the whole comments thing I cleaned up pretty quickly and forgot to upload the update, I'll release a large update with that and more soon.

But as for the 'Dynamic Options' you'd recommend creating them all? Ok then, thanks!

I dont suppose you could help me with another issue I'm having? It's for a System Monitor I'm creating. I'm having difficulty understanding the documentation on using !CommandMeasure to run a function in my Lua script. When I hit enter (Which runs the !CommandMeasure) after typing abc into the text input, the log spits out this error:

!CommandMeasure: Skin "abc" not found

Where am I going wrong? Here's my code for that part:

Code: Select all

[MeasureSettingsScript]
Measure=Script
ScriptFile=settings.lua
 
[TextColorInput]
Measure=Plugin
Plugin=InputText
Command1=[!CommandMeasure "MeasureSettingsScript" "changeColorFunc("$UserInput$")"]
FocusDismiss=1
DefaultValue=""
FontColor=#textColor#
SolidColor=#bgColor#
FontSize=16
FontFace=#Font#
X=20
H=23
Y=297
W=260
StringAlign=Center
MikeG621
Posts: 87
Joined: March 18th, 2013, 1:59 pm

Re: Dynamic Menu

Post by MikeG621 »

jam3sn wrote: I've read LUA can't physically create new meters, just change the values, so what would you suggest? Create a maximum of say 20 options, with 20 basic meters laid out but hidden until activated by the script?
In my calendar and notes skins I dynamically create meters using Lua and then if the number if items changes Hide/Show/create as necessary. The skin initially has just the static layout via includes and the required text fields, the Lua creates required number of meters to handle the tab Selected/Inactive states and some extra framing around the tab label. This particular skin is shown limited to 6 tabs, creating the measures [Tab1.State]/[Tab2.State]... and the meters [Label1.Outside]/[Label2.Outside]...

Code: Select all

function Initialize()
  _numberOfTabs = tonumber(SKIN:GetVariable('NumberOfTabs'))
  if _numberOfTabs > 6 then
    _numberOfTabs = 6
  end
  for i = 1, _numberOfTabs do
    local meter = 'Tab'..i..'.State'
    SKIN:Bang('!WriteKeyValue', meter, 'Measure', 'Calc')
    SKIN:Bang('!WriteKeyValue', meter, 'DynamicVariables', '1')
    if i == 1 then
      SKIN:Bang('!WriteKeyValue', meter, 'Formula', '(#*CurrentTab*# = 1 ? 1 : 0)')
    elseif i == _numberOfTabs then
      SKIN:Bang('!WriteKeyValue', meter, 'Formula', '(#*CurrentTab*# = '..i..' ? 1 : 2)')
    else
      SKIN:Bang('!WriteKeyValue', meter, 'Formula', '(#*CurrentTab*# = '..i..' ? 1 : (#*CurrentTab*# < '..i..' ? 2 : 0))')
    end

    meter = 'Tab'..i..'.Label.'
    SKIN:Bang('!WriteKeyValue', meter..'Outside', 'Meter', 'Image')
    SKIN:Bang('!WriteKeyValue', meter..'Outside', 'MeterStyle', 'Styles.LabelUnselectedOutside')
    SKIN:Bang('!WriteKeyValue', meter..'Outside', 'X', '(['Label:X]-(#*CurrentTab*#-'..i..'+1)*#*LabelShift*#-2+(['Tab'..i..'.State] = 2 ? (['Label.Top:W]+11) : 0))')
    SKIN:Bang('!WriteKeyValue', meter..'Outside', 'Y', '(['Label.Top:Y]+6)')
    SKIN:Bang('!WriteKeyValue', meter..'Outside', 'DynamicVariables', '1')
    SKIN:GetMeter(meter..'Outside'):Show()
  end

  for i = (numberOfTabs+1), 6 do
    if (SKIN:GetMeter('Tab'..i..'.Label'):GetOption('Meter')) == "Image" then
      SKIN:GetMeter('Tab'..i..'.Label.Outside'):Hide()
      --plus whatever else you need
    else
      break
      -- this is a Meter.Exists check. if the Label doesn't exist, GetOption() returns nil, which sends us to else and break the loop
  end
  Update()
)
Update() has the usual update stuff using !SetOption, !SetVariable or whatever else you need for the static fields. To switch tabs I use

Code: Select all

_tabIndex = tonumber(SKIN:GetVariable('CurrentTab'))
if _tabIndex > _tabTotal then
  _tabIndex = _tabTotal 
elseif _tabIndex < 1 then
  _tabIndex = 1
end

SKIN:Bang('!SetVariable', 'CurrentTab', _tabIndex)
for i = 1, _numberOfTabs do
  if i == _tabIndex then
    SKIN:GetMeter('Tab'..i..'.Label.Outside'):Hide()
    --and others...
  elseif i < _tabIndex then
    SKIN:Bang('!SetOption', 'Tab'..i..'.Label.Outside', 'ImageFlip',  'Horizontal')
    -- do whatever else you need to do it before you show it. I flip the image because I use it for left/right of the selected tab
    SKIN:GetMeter('Tab'..i..'.Label.Outside'):Show()
    -- etc
  else
    SKIN:Bang('!SetOption', 'Tab'..i..'.Label.Outside', 'ImageFlip', 'None')
    SKIN:GetMeter('Tab'..i..'.Label.Outside'):Show()
    -- etc
  end
end
All it needs in the skin is the variables #NumberOfTabs# and #CurrentTab# and a meter named [Label] to make all of those dynamic formulas work. The way I have the tabs working it uses a *lot* of moving meters, hence the crazy formulas I used in the X properties, the predefined Label meter has an equation that changes the X value according to #CurrentTab#, and also uses an equation in ClipStringW to allow the label to resize for as long as it needs without going past the skin boundary.

If you're just displaying a menu with a fixed size, I doubt you'd need the crazy positional formulas, but the framework is there to create the number of meters/measures you need on the fly.