[Lib] SplitButton()

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

[Lib] SplitButton()

31 Aug 2016, 17:46

Image

Here's a proof-of-concept stdlib function for creating split buttons (drop down buttons). Needs more options built in (changing the glyph, styles, etc) but its a start... Enjoy
Microsoft wrote:The Split Button is a composite control with which the user can select a default value bound to a primary button, or select from a list of mutually exclusive values displayed in a drop-down list bound to a secondary button.
Medium wrote:A split button has a default action followed by a divider line and an arrow. When the arrow is clicked it exposes a drop-down list of alternative actions. This pattern is used when there are many possible actions but only one primary action.
SplitButton ( hButton [, GlyphSize, MenuName ] )
- hButton = hWnd of button to turn into SplitButton
- GlyphSize = size of down arrow glyph (default: 16)
- MenuName = name of menu to call when clicked (default: SplitButton_Menu)

Issues/Requirements:
- statically saved hwnd of button from first call needs turned into array... for now only one button can be a SplitButton
- will conflict with other code using WM_Notify OnMessage()
- missing features from API for glyph size, imagelist, styles, etc...
- Requires Vista+, unsupported on XP

Function:

Code: Select all

SplitButton(hButton, GlyphSize=16, Menu="", hDontUse="") {
 Static     _ := OnMessage(0x4E, "SplitButton") ;WM_NOTIFY
 Static Menu_ := "SplitButton_Menu"
 Static hButton_
 If (Menu=0x4E)
 {
  hCtrl := NumGet(GlyphSize+0, 0, "Ptr") ;-> lParam -> NMHDR -> hCtrl
  If (hCtrl = hButton_) ;BCN_DROPDOWN for SplitButton
  {
   id := NumGet(GlyphSize+0, A_PtrSize * 2, "uInt")
   If (id = 0xFFFFFB20)
   {
    ControlGetPos, cX, cY, cW, cH,, ahk_id %hButton_%
    Menu, %Menu_%, Show, % cX+1, % cY + cH
   }
  }
 }
 Else ;Initialize
 {
  If (Menu <> "")
   Menu_ := Menu
  hButton_ := hButton
  Winset,   Style, +0x0C, ahk_id %hButton%          ;BS_SPLITBUTTON
  VarSetCapacity(   pBUTTON_SPLITINFO,  40, 0)
  NumPut(8,         pBUTTON_SPLITINFO,   0, "Int")  ;set glyph size
  NumPut(GlyphSize, pBUTTON_SPLITINFO,  4 + A_PtrSize * 2, "Int")
  SendMessage, BCM_SETSPLITINFO := 0x1607, 0, &pBUTTON_SPLITINFO, , ahk_id %hButton%
  Return
 }
}

 ;;parameters need to be expanded to cover these options:
 ;;BUTTON_SPLITINFO struct   ;INFO mask flags        ;STYLE flags
 ;;  UINT       mask;        ;BCSIF_GLYPH := 0x0001  ;BCSS_NOSPLIT         := 0x0001
 ;;  HIMAGELIST himlGlyph;   ;BCSIF_IMAGE := 0x0002  ;BCSS_STRETCH         := 0x0002
 ;;  UINT       uSplitStyle; ;BCSIF_STYLE := 0x0004  ;BCSS_ALIGNLEFT       := 0x0004
 ;;  SIZE       size;        ;BCSIF_SIZE  := 0x0008  ;BCSS_IMAGE           := 0x0008
Simple Example:

Code: Select all

Menu, SplitButton_Menu, Add, First Item, DoNothing
Menu, SplitButton_Menu, Add, Second Item, DoNothing
Gui, Add, Button, w160 h80 hwndhButton, Button
SplitButton(hButton)
Gui, Show
DoNothing:
Return
Pictured Example:

Code: Select all

;Demo of SplitButton()
#Include SplitButton.ahk

 Who = Google
 Menu, Menu, Add, Google, Menu
 Menu, Menu, Default, Google
 Menu, Menu, Add, Yahoo,  Menu
 Menu, Menu, Add, Bing,   Menu
 IfNotExist, %A_Temp%\google_favicon.ico
  UrlDownloadToFile, https://www.google.com/favicon.ico, %A_Temp%\google_favicon.ico
 IfNotExist, %A_Temp%\yahoo_favicon.ico
  UrlDownloadToFile, https://www.yahoo.com/favicon.ico,  %A_Temp%\yahoo_favicon.ico
 IfNotExist, %A_Temp%\bing_favicon.ico
  UrlDownloadToFile, https://www.bing.com/favicon.ico,   %A_Temp%\bing_favicon.ico
 Menu, Menu, Icon, Google, %A_Temp%\google_favicon.ico
 Menu, Menu, Icon, Yahoo,  %A_Temp%\yahoo_favicon.ico
 Menu, Menu, Icon, Bing,   %A_Temp%\bing_favicon.ico

 PartNumber := InputBox("Internet search...", "Search for:")
 PartNumber = %PartNumber%
 If (PartNumber = "")
  ExitApp
 Run, "https://www.%who%.com/#q=%PartNumber%"
ExitApp
Return

Menu:
 Who := A_ThisMenuItem
 GuiControl, , %hOK%, Search %Who%
 Menu, Menu, Default, %Who%
Return

InputBox(title, text, inputValue = "", owner=0,isPassword=0){
 Global


 GuiID := "InputBox"      ; If you change, also change the subroutines below for #GuiEscape & #GuiClose
 If( owner <> 0 ) {
 	Gui %owner%:+Disabled
 	Gui %GuiID%:+Owner%owner%
 }
   
 Gui, %GuiID%:+AlwaysOnTop -MinimizeBox -MaximizeBox +HwndGuiHwnd +E0x400 -0x80000 +Label%GuiID%
 Gui, %GuiID%:+LastFound
 Gui, %GuiID%:Color, White

 Gui, %GuiID%:Add, Text, 	x-5  y148 w10 h10  -BackgroundTrans hwndGray -Border +0x2000000
 Gui, %GuiID%:Add, Progress, 	x-5  y147 w10 h3    BackgroundDFDFDF hwndDarkGrayLine

 Gui, %GuiID%:Font, s12 w400, Segoe UI
 Gui, %GuiID%:Add, Text, x20 y20 w320 c003399 BackgroundTrans, %text%

 Gui, %GuiID%:Font, s9 w0, Segoe UI
 Gui, %GuiID%:Add, Edit, % "x12 y+23 w333 R1 hwndhMyCombo -Sort v_cInput_Value" . ((isPassword <> 0) ? " Password":"")
 
 Gui, %GuiID%:Add, Button, x100 y+28 w120 h40 gCInputButton Default hwndhOK, % "Search Google" 	;+0x0C for BS_SPLITBUTTON doesn't work here

 SplitButton(hOK,24,"Menu")

 Gui, %GuiID%:Add, Button, x228 y+-40 w120 h40 gCInputButtonCancel, % "Cancel"


 Gui %GuiID%:Show,Hide,%title%
 WinGetPos, X, Y, Width, Height
 GuiControl, %GuiID%:Move, % DarkGrayLine, % "w" Width+10 " y" Height-87
 GuiControl, %GuiID%:Move, % Gray, % "w" Width+10 " h" Height-100 " y" Height-86
 Control, ExStyle, -0x20000, msctls_progress321

 Gui %GuiID%:Show

 Loop
 {
  If( _CInput_Result )
   Break
  Sleep, 10
 }
 If( owner <> 0 )
  Gui %owner%:-Disabled
 Gui, %GuiID%:Submit, Hide

 if InStr(_CInput_Result, "Search") {
  Result := _cInput_Value
 } else {
  Result := ""
 }
 _cInput_Value := ""
 _CInput_Result := ""
 Gui %GuiID%:Destroy
Return Result
 CInputButton:
  StringReplace _CInput_Result, A_GuiControl, &,, All
 Return
 InputBoxGuiEscape:
 InputBoxGuiClose:
  _CInput_Result := "Close"
  ExitApp
 Return
 cInputButtonCancel:
  ExitApp
 Return
}
Last edited by gwarble on 01 Sep 2016, 20:24, edited 10 times in total.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

31 Aug 2016, 18:23

Oops, I left the "GetDlgItem" call hard coded to the 7th control from my example... need to fix that, gimme a minute (i guess i'll save (static var) the hCtrl from the initialization call and compare that way instead, maybe an array of hCtrl and its associated menu name in case there are more than one, have to think whats best)
Last edited by gwarble on 01 Sep 2016, 12:51, edited 1 time in total.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
Notus
Posts: 7
Joined: 14 Jun 2016, 19:58

Re: [Lib] SplitButton()

31 Aug 2016, 20:02

Ahhh I love it.
Thanks for sharing :)
gregster
Posts: 8921
Joined: 30 Sep 2013, 06:48

Re: [Lib] SplitButton()

31 Aug 2016, 20:15

Hmmm, doesn't show any dropdown for me. I guess, it only supports 32 bit at the moment?

Edit: Oh yeah, 32 bit works. Looks very useful. Thanks a lot!
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

31 Aug 2016, 20:36

Notus wrote:Ahhh I love it.
Thanks for sharing :)
You're welcome... still needs improvement but might come in handy
gregster wrote:Hmmm, doesn't show any dropdown for me. I guess, it only supports 32 bit at the moment?

Edit: Oh yeah, 32 bit works. Looks very useful. Thanks a lot!
Oops, yeah I forgot to test on 64bit... should be trivial, i will look into it Fixed - tested working on U64, U32, and A32
Last edited by gwarble on 01 Sep 2016, 13:27, edited 2 times in total.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Lib] SplitButton()

01 Sep 2016, 03:11

Seemingly, this button can be realized as a Custom control rather simply, though you might need to specify the height in most cases. The most important advantage is, that you don't have to monitor WM_NOTIFY messages.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

01 Sep 2016, 13:00

just me wrote:Seemingly, this button can be realized as a Custom control rather simply, though you might need to specify the height in most cases. The most important advantage is, that you don't have to monitor WM_NOTIFY messages.
Oh really? I haven't played around with Gui, Add, Custom... controls before. Not having to monitor WM_NOTIFY is a worthy advantage, but how would you assign/show the menu?
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: [Lib] SplitButton()

01 Sep 2016, 23:28

gwarble wrote:
just me wrote:Seemingly, this button can be realized as a Custom control rather simply, though you might need to specify the height in most cases. The most important advantage is, that you don't have to monitor WM_NOTIFY messages.
Oh really? I haven't played around with Gui, Add, Custom... controls before. Not having to monitor WM_NOTIFY is a worthy advantage, but how would you assign/show the menu?
Nice! This is my trying by reading https://autohotkey.com/docs/commands/Gu ... htm#Custom:

Code: Select all

BS_DEFSPLITBUTTON := 0x000D
BCN_DROPDOWN := -1248

Gui, Add, Custom, ClassButton w300 h100 %BS_DEFSPLITBUTTON% gSplitBtnEvent HWNDhBtn, test
Gui, Show
return

SplitBtnEvent:
	if (A_GuiEvent = "N") {
		nmhdr_code := NumGet(A_EventInfo + 2*A_PtrSize, "int")
		if (nmhdr_code != BCN_DROPDOWN)
			return

		; hCtrl := NumGet(A_EventInfo, "ptr")
		; if (hCtrl != hBtn)
		; 	return

		ControlGetPos, cX, cY, cW, cH,, ahk_id %hBtn%

		Menu, menu1, Add, item 1, MenuOnSelect
		Menu, menu1, Add, item 2, MenuOnSelect
		Menu, menu1, Show, % cX + 1, % cY + cH
		Menu, menu1, Delete
	} else {
		MsgBox, Normal Click
	}
return

MenuOnSelect:
	MsgBox, % A_ThisMenuItem
return

GuiClose:
ExitApp
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

02 Sep 2016, 16:36

tmplinshi wrote:Nice! This is my trying by reading https://autohotkey.com/docs/commands/Gu ... htm#Custom:
Nice, thanks for sharing... Maybe this method can be used to create a "SizeGrip" control, which is really a ScrollBar class i believe, which I've been meaning to get working
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Lib] SplitButton()

02 Sep 2016, 16:58

Really interesting.
In UCR, all of my Input Selection GUIControls (Controls to select button / axis bindings etc) are DDLs with "Cue Banners".
However, because UCR has a scrolling GUI, you could unintentionally change the values of a DDL when you meant to scroll the GUI. So in order to fix that, I had to de-parent the list from the DDL, which was a nasty hacky solution. Also the user could type in the EditBox part of the DDL, which caused issues too.
At some point I had planned to build a new custom GUIControl, but this looks like basically what I would want.

However, I would need to be able to have many, many splitbuttons active at the same time, so I think some work would be required for that.

These are some of the GuiControls / Menus that I would be trying to replace with your system
Spoiler
FYI, the reason I compact all the functionality for a binding type into one GuiControl class is because UCR is powered by plugins, which are just AHK classes that can add these custom GuiControls using an AHK-like syntax.
For example, to add an Input Axis GuiControl, the syntax would be like:
this.AddInputAxis("InputAxis", 0, this.MyInputChangedState.Bind(this), "xm w125")
If it were more than one GuiControl, positioning and sizing of the controls would quickly become a nightmare, so "compound GuiControls" like this are of great interest to me!
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

02 Sep 2016, 18:08

Cool, yeah it might work well for your program, although its really more intended for the primary function acting like a button which might not apply but it wouldn't really hurt anything

Just an FYI, this isn't really "my system", its a new button style in windows vista+ (common controls 6.0+ i believe) that hasn't been wrapped natively by AutoHotkey but is built in to windows.

I'll make a "multiple button" hack for you this evening so you can experiment with more than one SplitButton... also the fact that a drop down menu is shown when the split part is clicked is really a matter of consistency with documentation/existing examples... in reality you can generate one of your "fake" menus (the panel with other controls that looks like it has menu behavior... Similar to how my EitherMouse gui was designed...
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Lib] SplitButton()

03 Sep 2016, 07:03

Well, I ripped out hButton_, it didn't seem to serve any purpose

Here is my class so far:
You pass it a standard AHK option string, the text for the button, and two boundfuncs (One for when the button is clicked, and one for when the glyph is clicked), and it handles the rest.
If you need the hwnd of the button, it is the .hwnd property of the class instance.

Code: Select all

#SingleInstance force

Menu, SplitButton_Menu1, Add, 1 - First Item, DoNothing
Menu, SplitButton_Menu1, Add, 1 - Second Item, DoNothing
Menu, SplitButton_Menu2, Add, 2 - First Item, DoNothing
Menu, SplitButton_Menu2, Add, 2 - Second Item, DoNothing
Buttons := []
Buttons.push(new SplitButton("w160 h80", "Button1", Func("ButtonClicked").Bind(1), Func("GlyphClicked").Bind(1)))
Buttons.push(new SplitButton("w160 h80", "Button2", Func("ButtonClicked").Bind(2), Func("GlyphClicked").Bind(2)))
Gui, Show
return

ButtonClicked(id){
    MsgBox % "You clicked button " id
}

GlyphClicked(id){
    global Buttons
    ControlGetPos, cX, cY, cW, cH,, % "ahk_id " Buttons[id].hwnd
    Menu, % "SplitButton_Menu" id, Show, % cX+1, % cY + cH
}

DoNothing:
Return

GuiClose:
    ExitApp

Class SplitButton {
    __New(options, text, ClickCallback, GlyphCallback, GlyphSize=16) {
        Gui, Add, Button, % options " hwndhwnd", % text
        GuiControl, +g, % hwnd, % ClickCallback
        this.hwnd := hwnd, this.ClickCallback := ClickCallback, this.GlyphCallback := GlyphCallback, this.GlyphSize := GlyphSize
        Winset,   Style, +0x0C, ahk_id %hwnd%          ;BS_SPLITBUTTON
        VarSetCapacity(   pBUTTON_SPLITINFO,  40, 0)
        NumPut(8,         pBUTTON_SPLITINFO,   0, "Int")  ;set glyph size
        NumPut(GlyphSize, pBUTTON_SPLITINFO,  4 + A_PtrSize * 2, "Int")
        SendMessage, BCM_SETSPLITINFO := 0x1607, 0, &pBUTTON_SPLITINFO, , ahk_id %hwnd%
        OnMessage(0x4E, this.WM_NOTIFY.Bind(this)) ;WM_NOTIFY
    }
    
    WM_NOTIFY(wParam,lParam,msg,hwnd){
        hCtrl := NumGet(lParam+0, 0, "Ptr") ;-> lParam -> NMHDR -> hCtrl
        If (hCtrl = this.hwnd){ ;BCN_DROPDOWN for SplitButton
            If (NumGet(lParam+0, A_PtrSize * 2, "uInt") = 0xFFFFFB20){
                this.GlyphCallback.Call()
            }
        }
    }
}
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

03 Sep 2016, 13:44

cool, nice work...

hButton_ was in there to save the hWnd from the initialization call into a static variable, so when the OnMessage call comes around the control's hWnd could be compared to the one initialized... since you are saving into a global class object its not really necessary, and my plan (for the simple lib function) was to make it into a fake array of controls with their own assigned menu names and/or labels to gosub
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
hughman
Posts: 18
Joined: 17 Jun 2014, 09:39

Re: [Lib] SplitButton()

10 Feb 2017, 07:07

Is it possible to make it work on XP?
Or is there another method to implement the same function?
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: [Lib] SplitButton()

14 Feb 2017, 15:14

No XP doesn't support that button style... you'd have to replicate the functionality yourself (if you google it you will see others' implementations in other languages)
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
hughman
Posts: 18
Joined: 17 Jun 2014, 09:39

Re: [Lib] SplitButton()

16 Feb 2017, 06:22

gwarble wrote:No XP doesn't support that button style... you'd have to replicate the functionality yourself (if you google it you will see others' implementations in other languages)
I have an idea to simulate this style via BCM_SETIMAGELIST on XP
What I need to do is to make server ico of diffent status and make it right alignment inside the button.
It's luckly that majkinetor have wrote the function named ILButton long ago, from his famous gui forms

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 122 guests