It is currently March 29th, 2024, 2:41 pm

Custom [Strike]Skin[\Strike] Context Menu

Tips and Tricks from the Rainmeter Community
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

I recently skimmed through a topic where the op was asking about a nested context menu. So, I thought why not just make one.

v1.0.1

v2.0.0
So before diving in a few pros and cons:
Pros:
  • Nested contexts
  • Highly customizable
  • Static or dynamic menu items, with in-context variables
  • Icons in context menu(highly recommended to use similar icons, preferably from icons8)
Cons:
  • Have to add "Config" parameter to every Bang
  • File paths are relative to the custom menu skin path, naturally(Using #ROOTCONFIGPATH# is preferable)
Now the real thing:
So all you have to do is to make a script measure and add items to it like you do in Rainmeter section.
So the options go like this:

Code: Select all

[CustomMenu]
Measure=Script
ScriptFile=#ROOTCONFIGPATH#CustomMenu\CustomMenu.lua

; ----------------- Context options
Title1 = <title1>
Icon1 = <path2icon1> 
Option1 = <option1>
Title2 = <title2>
Icon1 = <path2icon2> 
Option2 = <option2>

;; ----------------- Nested context menu
Title3 = <title3>
Icon1 = <path2icon3> 
Title3Subtitle1 = <subcontext3title1>
Title3Icon1 = <subcontext3icon1> 
Title3Option1 = <subcontext3option1>
Title3Subtitle2 = <subcontext3title2>
Title3Icon2 = <subcontext3icon2>
Title3Option2 = <subcontext3option2>
...
Title3Subtitle6 = <subcontext3title6>
Title3Icon6 = <subcontext3icon6>
Title3Option6 = <subcontext3option6>
;; -----------------------------------

...

;; ---------------- Divider (Use --- as TitleN)
Title7 = ---
;; -----------------------------------

...

Title15 = <title15>
Icon15 = <path2icon15> 
Option15 = <option15>
UpdateDivider = -1
Yes, max items are 15 and max nested items are 6. But you can overide them by editing the lua and changing the for loops' terminal value. Tho not recommended.
v2 perks:

Now how to call it:

Code: Select all

RightMouseUpAction=[!ActivateConfig "#ROOTCONFIG#\CustomMenu"]
LoL that's it.
Now how the hell is it dynamic(the real deal):
  • So, in v2, you can have different measures(preferably Calc or String) and define context options in them. Then call the function Make('measureName') to make a new context menu right away.
    For example:

    Code: Select all

    ; main function
    [CustomMenu]
    Measure=Script
    Script=#ROOTCONFIGPATH#CustomMenu\CustomMenu.lua
    UpdateDivider=-1
    
    [ThemeSelector]
    Measure=Calc
    Title1=Dark
    Title2=Solarized Light
    Title3=Material Dark
    Option1=[!WriteKeyValue Variables Theme "Dark" "#@#UserSettings.inc"][!Refresh "#CURRENTCONFIG#"]
    Option2=[!WriteKeyValue Variables Theme "SolarizedLight" "#@#UserSettings.inc"][!Refresh "#CURRENTCONFIG#"]
    Option3=[!WriteKeyValue Variables Theme "MaterialDark" "#@#UserSettings.inc"][!Refresh "#CURRENTCONFIG#"]
    
    and you can call it by:

    Code: Select all

    [ThemeSelectorMeter]
    ...
    LeftMouseUpAction=[!CommandMeasure CustomMenu "Make('ThemeSelector')"][!ActivateConfig "#ROOTCONFIG#\CustomMenu"]
    
    P.S.: You have to call "Initialize()" if you used "Make('someMeasure')" to show the context menu in parent measure. And while using Make(), always use single quotes, e.g Make('measureName').
  • Another is in-context variables. You can dynamically change what to change.

    Syntax:

    Code: Select all

    [MeasureName]
    ...
    ; for N between 1 and 9
    VariableN=<some string>
    TitleX=<some string> $N <some string>
    OptionX=<some string> $N <some string>
    
    P.S.:
    • This is not valid for IconN or TitleXIconN options.
    • You can have total 9 variables, from Variable1 to Variable9
    • To use $N as is, escape it with backslash("\"), e.g. "\$1" will be written "$1", without the quotes.


    For example:

    Code: Select all

    [CustomMenu]
    Measure=Script
    ScriptFile=#ROOTCONFIGPATH#
    UpdateDivider=-1
    Variable1=
    Variable2=#CURRENTCONFIG#
    Variable3=#CURRENTPATH##CURRENTFILE#
    Title1=Calibri
    Title2=Noto Sans
    Title3=Roboto
    Icon1=#@#Icons\font
    Icon2=#@#Icons\font
    Icon3=#@#Icons\font
    Option1=[!SetOption $1 FontFace "Calibri" "$1"][!WriteKeyValue $1 FontFace "Calibri" "$2"][!UpdateMeter "$1" "$2"][!Redraw "$2"]
    Option2=[!SetOption $1 FontFace "Noto Sans" "$1"][!WriteKeyValue $1 FontFace "Calibri" "$2"][!UpdateMeter "$1" "$2"][!Redraw "$2"]
    Option3=[!SetOption $1 FontFace "Roboto" "$1"][!WriteKeyValue $1 FontFace "Calibri" "$2"][!UpdateMeter "$1" "$2"][!Redraw "$2"]
    Now you can do thing like:

    Code: Select all

    [MeterX]
    ...
    RightMouseUpAction=[!SetOption CustomMenu Variable1 "#CURRENTSECTION#"][!CommandMeasure CustomMenu "Initialize()"][!ActivateConfig "#ROOTCONFIG#\CustomMenu"]
    
    [MeterY]
    ...
    RightMouseUpAction=[!SetOption CustomMenu Variable1 "#CURRENTSECTION#"][!CommandMeasure CustomMenu "Initialize()"][!ActivateConfig "#ROOTCONFIG#\CustomMenu"]
    
    [MeterZ]
    ...
    RightMouseUpAction=[!SetOption CustomMenu Variable1 "#CURRENTSECTION#"][!CommandMeasure CustomMenu "Initialize()"][!ActivateConfig "#ROOTCONFIG#\CustomMenu"]
Changing appearance:
Edit the CustomMenu.style file to change the appearance of the menu. All the variables and meter styles are in there. The names are not that confusing. And keep the "*#Scale#"s intact. I will update it later to make them easier to change.
Preview of example skin given below:
Click to animate
ezgif.com-gif-maker.gif
Example skin:
ASimpleSuite_2.0.1.rmskin
Changes in v2.0.0
  • Nested context going off screen, now it stays within work area(multi monitor support not yet, cause I have only one lol).
  • No executables this time, added a plugin instead for the purpose.
  • Lessen the amount of activation syntax.
  • v2 perks stated above
Changes in v2.0.1
  • Fixed a bug with keeping the menu inside workarea horizontally.
You do not have the required permissions to view the files attached to this post.
Last edited by death.crafter on November 5th, 2021, 11:44 am, edited 3 times in total.
from the Realm of Death
User avatar
Active Colors
Moderator
Posts: 1251
Joined: February 16th, 2012, 3:32 am
Location: Berlin, Germany

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by Active Colors »

Wow, very nice! I was thinking sometime before about making my own context menu simulation for one of my skins but you've done the job for me. If I find myself implementing it I would let you know :D
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

Active Colors wrote: July 21st, 2021, 5:48 pm Wow, very nice! I was thinking sometime before about making my own context menu simulation for one of my skins but you've done the job for me. If I find myself implementing it I would let you know :D
I need to update the skin, regarding nested menu going off screen(thanks to Yincognito).

Thanks for the compliment. :-D

Edit: Updated :P
from the Realm of Death
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

Yincognito wrote:
So, I figured out how to solve the problem horizontally, but I can't do so vertically. Can you help me a little out here?

I could have done it if the height of the container were not to be 0 from the beggining. Since I use a formula for the container's height, when I access it on OnRefreshAction, it ends up being 0. I just can't think of another way of getting it from the beggining.

Though I quoted Yincognito, anyone's help is appreciated.

Thanks in advance.
from the Realm of Death
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by Yincognito »

death.crafter wrote: July 22nd, 2021, 11:57 am So, I figured out how to solve the problem horizontally, but I can't do so vertically. Can you help me a little out here?

I could have done it if the height of the container were not to be 0 from the beggining. Since I use a formula for the container's height, when I access it on OnRefreshAction, it ends up being 0. I just can't think of another way of getting it from the beggining.

Though I quoted Yincognito, anyone's help is appreciated.

Thanks in advance.
I didn't look into your setup in detail yet, but what I can tell is that I had a somewhat similar system of using formulas for the "child skin" (I suppose this is more or less your container, right?) size in my custom tooltip approach, and although I did this exclusively through native Rainmeter, there are a couple of things I did - maybe they can give you an idea regarding solving this:
- I used small !Delay-s to give time to Rainmeter to provide the needed info or do its thing (I'm just mentioning this, mostly it's not needed)
- I used skin-to-skin !SetVariables (didn't use !WriteKeyValue-s at all, btw, much faster using memory than disk) to "communicate" info between skins
- I first updated the meters of the child skin from the parent skin, did a redraw, and only then updated the measure (in my case) that handled the child skin movement (using parent skin specific positioning); I didn't use OnRefreshAction to move the child skin, but a measure, given the dynamic variables and the easiness of controlling it (otherwise the option is theoretically ok, since it runs after the 1st skin update anyway)
- I used an additional variable (1 or 0) passed from the parent skin to the child one to make sure the movement measure from the child skin moved that skin ONLY after it received the "ok" from the variable, meaning the meters from the child skin corrected that skin's position and size accordingly
- in the movement measure, I performed the above variable check in an IfCondition, and moved the skin in the IfTrueAction(s)

In principle, assuming there isn't any other "issue" affecting this, it's about the order of the operations: strict control of that makes sure you first draw the child skin normally, i.e. in its "unmoved" position (easier if that skin is by default hidden, by the way, so you don't see it shifting when displaying it), get the correct info about its position / dimensions, and the last thing you do is to move it in the "final" position.

That being said, it would help if you could pinpoint exactly the key parts that you have a problem with, so I don't have to look for the needle in the haystack in a code I'm not (yet) familiar with. :D

P.S. Sorry for the delay, been doing other "mystery solving" stuff before, LOL. One step at a time for me, it's clearer that way...
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

Yincognito wrote: July 22nd, 2021, 4:23 pm That being said, it would help if you could pinpoint exactly the key parts that you have a problem with, so I don't have to look for the needle in the haystack in a code I'm not (yet) familiar with. :D
I guess I should have explained better.

So for vertical positioning, here are the variables.
  • [#CURRENTCONFIGY]
  • [PARENT_OPTION:Y] - (where the child context container is supposed to materialize)
  • [CHILD_OPTION_FIRST_ITEM:Y] - (the first no height meter in a child container)
  • [CHILD_OPTION_LAST_ITEM:Y] - (the last no height meter in a child container)
  • [#WORKAREAHEIGHT]
What I am trying to do:
# *{the height of CHILD_CONTAINER}
# **{the distance from end of screen to the parent option}

if *([CHILD_OPTION_LAST_ITEM:Y]-[CHILD_OPTION_LAST_ITEM:Y]) greater than **([#WORKAREAHEIGHT]-[PARENT_OPTION:Y]) then

[PARENT_OPTION:Y]-(([CHILD_OPTION_LAST_ITEM:Y]-[CHILD_OPTION_FIRST_ITEM:Y])-([#WORKAREAHEIGHT]-[PARENT_OPTION:Y]))

And I think I might be able to get it in the mean time.

Thanks for taking your time to reply.
Yincognito wrote: July 22nd, 2021, 4:23 pm P.S. Sorry for the delay, been doing other "mystery solving" stuff before, LOL. One step at a time for me, it's clearer that way...
You don't have to be sorry for such things.
from the Realm of Death
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by Yincognito »

death.crafter wrote: July 22nd, 2021, 4:58 pm I guess I should have explained better.

So for vertical positioning, here are the variables.
  • [#CURRENTCONFIGY]
  • [PARENT_OPTION:Y] - (where the child context container is supposed to materialize)
  • [CHILD_OPTION_FIRST_ITEM:Y] - (the first no height meter in a child container)
  • [CHILD_OPTION_LAST_ITEM:Y] - (the last no height meter in a child container)
  • [#WORKAREAHEIGHT]
What I am trying to do:
# *{the height of CHILD_CONTAINER}
# **{the distance from end of screen to the parent option}

if *([CHILD_OPTION_LAST_ITEM:Y]-[CHILD_OPTION_LAST_ITEM:Y]) greater than **([#WORKAREAHEIGHT]-[PARENT_OPTION:Y]) then

[PARENT_OPTION:Y]-(([CHILD_OPTION_LAST_ITEM:Y]-[CHILD_OPTION_FIRST_ITEM:Y])-([#WORKAREAHEIGHT]-[PARENT_OPTION:Y]))

And I think I might be able to get it in the mean time.

Thanks for taking your time to reply.



You don't have to be sorry for such things.
What I meant was more of INI / section / option whereabouts, but anyway, I sort of understood what you mean in pseudocode, and found the incriminated place(s) - I was looking in the wrong place up until now. Not a fan of writing stuff from Lua in the INI / INC files behind the user's back at all, much harder to debug and find which is which and which is controlled from where this way, especially if you're not the author.

After trying in vain to write stuff like normal people in the INI-s only to see them overwritten, I tried my best to do a:

Code: Select all

                local containerY = '(#MouseY# < (#SCREENAREAHEIGHT#-(500)) ? ([ContextOption'..i..':Y]) : (#SCREENAREAHEIGHT#-(500)))'
                table.insert(t, '\n[ContextOption'..i..'Background]\nMeter=Shape\nX='..containerX..'\nY='..containerY..'\nColor=Fill Color #SubContextBackgroundColor#\nShape='..rectangle..'\nHidden=1\nGroup=ContextOption'..i..'\nMouseLeaveAction=[!HideMeterGroup ContextOption'..i..'][!Redraw]')
test in your Lua, but while the main menu moved, I have no f...g clue where to move the submenus from. :confused:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

Yincognito wrote: July 22nd, 2021, 6:41 pm

Code: Select all

                local containerY = '(#MouseY# < (#SCREENAREAHEIGHT#-(500)) ? ([ContextOption'..i..':Y]) : (#SCREENAREAHEIGHT#-(500)))'
                table.insert(t, '\n[ContextOption'..i..'Background]\nMeter=Shape\nX='..containerX..'\nY='..containerY..'\nColor=Fill Color #SubContextBackgroundColor#\nShape='..rectangle..'\nHidden=1\nGroup=ContextOption'..i..'\nMouseLeaveAction=[!HideMeterGroup ContextOption'..i..'][!Redraw]')
test in your Lua, but while the main menu moved, I have no f...g clue where to move the submenus from. :confused:
Lol I am so sorry for making you work hard.

Just leave it be. I will think of something in the mean time. But even so it will be impossible to fully replicate a context menu. It's just a stand-in after all.

Thanks a lot though.
Since I didn't intend the user to look into the internal workings, I didn't bother with a cleaner code. Somewhat my fault.
from the Realm of Death
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by Yincognito »

death.crafter wrote: July 22nd, 2021, 7:18 pm Lol I am so sorry for making you work hard.

Just leave it be. I will think of something in the mean time. But even so it will be impossible to fully replicate a context menu. It's just a stand-in after all.

Thanks a lot though.
Since I didn't intend the user to look into the internal workings, I didn't bother with a cleaner code. Somewhat my fault.
Nah, working is not a problem, finding WHAT to work is one, in this case.
This:

Code: Select all

                local containerY = '(#MouseY# < (#SCREENAREAHEIGHT#-(300)) ? ([ContextOption'..i..':Y]) : ([ContextContainer:Y]+[ContextOption'..i..':Y]-([ContextOption7Last:Y]-[ContextOption7Background:Y])))'
                table.insert(t, '\n[ContextOption'..i..'Background]\nMeter=Shape\nX='..containerX..'\nY='..containerY..'\nColor=Fill Color #SubContextBackgroundColor#\nShape='..rectangle..'\nHidden=1\nGroup=ContextOption'..i..'\nMouseLeaveAction=[!HideMeterGroup ContextOption'..i..'][!Redraw]')
will actually work in setting the Y of the child context menu, but there are 2 problems:
- replacing the testing 300 value with the (I think) proper [ContextOption7Last:Y]-[ContextOption7Background:Y] doesn't work
- the background of the child menu will be taller and blank, you probably know better why that happens
These, unless you have a better idea why they happen, might be because things are initially hidden, although I'm not sure why setting the Y works just fine by using the same values (by the way, that Y setting must take the height of a main menu "line"/"item" into account as well, but I had no idea what was that value, so I skipped it)... :???:

P.S. I don't believe in impossible things, that's the rest of the world's opinion. I know for sure things are possible if one really wants so (and, of course, has the brain for it, which you do).
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
death.crafter
Rainmeter Sage
Posts: 1399
Joined: April 24th, 2021, 8:13 pm

Re: Custom [Strike]Skin[\Strike] Context Menu

Post by death.crafter »

Yincognito wrote: July 22nd, 2021, 7:54 pm - replacing the testing 300 value with the (I think) proper [ContextOption7Last:Y]-[ContextOption7Background:Y] doesn't work
Actually you are setting the Y of ContextOption7Background so you can't use [ContextOption7Background:Y]. For that purpose I made a First item for every child container.

[ContextOption7First:Y] would do the trick if you want to get the height.

But I am not feeling like working on this now. So I will try the above way tomorrow and let you know.
from the Realm of Death