forked from ChrisS85/CGUI
-
Notifications
You must be signed in to change notification settings - Fork 5
/
CGUI.ahk
1403 lines (1269 loc) · 47.6 KB
/
CGUI.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#NoEnv ;Leave this here if you don't want weird ListView icon behavior (and possibly other side effects)
/*
Class: CGUI
The main GUI class. User created GUIs need to extend this class and call Base.__New() in their constructor before doing anything related to this class.
Property: Accessing Controls
Controls may be accessed by their name by using GUI.Name or by their window handle by using GUI.Controls[hwnd] (assuming Name is a string and hwnd is a variable).
The difference between these two methods is that controls which are added as sub-controls to other controls are not accessible by their name through the main GUI object. They can either be accessed by hwnd like described above or by GUI.ParentControl.Controls.SubControlName (again assuming that SubControlName is a string).
*/
Class CGUI
{
static GUIList := {}
static EventQueue := []
static WindowMessageListeners := []
static RegisteredControls := {}
;~ _ := {} ;Proxy object
/*
Get only:
var Controls := {}
var hwnd := 0
var GUINum := 0
MinMax
Instances ;Returns a list of instances of this window class
var CloseOnEscape := 0 ;If true, pressing escape will call the PreClose() event function if defined. Otherwise, it will call Escape() if it is defined.
var DestroyOnClose := 0 ;If true, the gui will be destroyed instead of being hidden when it gets closed by the user.
Not supported:
var Label := "CGUI_" ;Labels are handled internally and get rerouted to event functions defined in the class which extends CGUI
var LastFoundExist := 0 ;This is not needed because the GUI is created anyway when the class gets instantiated.
Event functions that can be defined in the class that extends CGUI:
Size(Event) ;Called when window size changes
;Possible values for Event:
;0: The window has been restored, or resized normally such as by dragging its edges.
;1: The window has been minimized.
;2: The window has been maximized.
ContextMenu() ;Called when a context menu is about to be invoked. This is mostly useless for now because the control can not get identified properly
DropFiles() ;Called when files were dropped on the gui. This is mostly useless for now because the control can not get identified properly
PreClose() ;Called when the window is about to be closed or when Escape was pressed and CloseOnEscape = true. If it returns true, the window is kept open. Otherwise it will be hidden or destroyed depending on the value of DestroyOnClose
PostDestroy() ;Called when the window was destroyed. Attention: Many variables and functions in this object aren't usable anymore. This function is mostly used to release additional resources or to exit the program.
Escape() ;Called when escape is pressed and CloseOnEscape = false. The window is not automatically hidden/destroyed when CloseOnEscape = false.
*/
;The _Constructor key is never actually assigned (see __Set()). This line simply calls the __New() constructor of CGUI so that window classes deriving from CGUI do not need to call the base constructor.
_Constructor := this.base.base.__New(this)
;Used for keeping track of the window size. Until the window is shown for the first time or manual size is set
;it will keep updating the size of the (invisible) window when new controls are added.
_Initialized := false
/*
Event Handler: OnClose(Source)
Invoked when the window is closed (hidden or destroyed).
Event Handler: OnDestroy(Source)
Invoked when the window is destroyed.
*/
OnClose := new EventHandler()
OnDestroy := new EventHandler()
__New(instance)
{
if(!CGUI_Assert(IsObject(instance) && !instance.HasKey("hwnd"), "CGUI constructor must not be called!"))
return
this.Insert("_", {}) ;Create proxy object to store some keys in it and still trigger __Get and __Set
start := 1
loop {
GUINum := instance.__Class start
Gui %GUINum%:+LastFoundExist
IfWinNotExist
{
instance.GUINum := GUINum
break
}
start++
}
if(!instance.GUINum) ;Should not happen unless instance uses a faulty __Set() mechanism
return
instance.Controls := {}
instance.Font := new CFont(instance.GUINum)
CGUI.GUIList[instance.GUINum] := instance
GUI, % instance.GUINum ":+LabelCGUI_ +LastFound"
instance.hwnd := WinExist()
instance._.Delimiter := "|"
/*
Prepare for some HAX!
The code below enables the user of this library to create subclasses inside the GUI class that represent controls.
It scans through the object and looks for fitting subclasses.
Then a an object based on a copy of a subclass is created and a control with the params specified in the subclass is created.
The base of the copied subclass is changed to the newly created control object so that the functions in the subclass can access the control object through the this keyword.
Event functions are also preferably routed to subclasses.
*/
for key, Value in instance.base
{
if(IsObject(Value) && Value.HasKey("__Class") && Value.HasKey("Type") && Value.HasKey("Options") && Value.HasKey("Text")) ;Look for classes that define a type property
{
;~ if(!CGUI_Assert(Value.Type != "", "Control class definitions must use static properties."))
;~ continue
Name := Value.HasKey("Name") ? Value.Name : SubStr(Value.__Class, InStr(Value.__Class, ".") + 1)
control := instance.AddControl(Value.Type, Name, Value.Options, Value.Text)
instance[Name] := {base : ObjClone(Value)}
instance[Name].base.base := control
instance.Controls[instance[Name].hwnd] := instance[Name]
if(IsFunc(instance[Name].__New) = 3)
instance[Name].__New(instance)
else if(IsFunc(instance[Name].__New) = 2)
instance[Name].__New()
if(IsFunc(instance[Name].__Init))
instance[Name].__Init()
}
}
Gui, % instance.GUINum ":Show", Hide Autosize Center
;~ WinGetPos, x, y, w, h, % "ahk_id " instance.hwnd
;~ msgbox % "w " w " h " h
;Register for WM_COMMAND and WM_NOTIFY messages since they are commonly used for various purposes.
;~ instance.OnMessage(WM_COMMAND := 0x111, "HandleInternalMessage")
;~ instance.OnMessage(WM_NOTIFY := 0x004E, "HandleInternalMessage")
}
;This class handles window message routing to the instances of window classes that register for a specific window message
Class WindowMessageHandler
{
static WindowMessageListeners := [] ;This object stores instances of this class that are associated with a specific window message. The instances keep records of all windows that listen to this message.
__New(Message)
{
this.Message := Message
this.Listeners := [] ;Array containing all window classes that listen to Message.
this.ListenerCount := 0 ;Number of all window class instances that are listening to a message
}
/*
Registers a window instance as a listener to a window message.
If hwnd is an object, it represents a window object that is handled separately for internal messages.
*/
RegisterListener(Message, hwnd, FunctionName)
{
;Don't allow calling this function on the contained instances
if(IsObject(this.base) && this.base.__Class = this.__Class)
return
if(hwnd > 0)
GUI := CGUI.GUIFromHWND(hwnd)
else if(IsObject(hwnd)) ;Support internal window messages by storing them with a hwnd value of zero.
{
GUI := hwnd
hwnd := 0
}
;if parameters are valid and the listener isn't registered yet, add it and possibly set up the OnMessage Callback
if(Message && GUI && FunctionName && IsFunc(GUI[FunctionName]))
{
;If the current message hasn't been registered anywhere
if(!this.WindowMessageListeners.HasKey(Message))
{
this.WindowMessageListeners[Message] := new this(Message)
OnMessage(Message, "CGUI_WindowMessageHandler")
}
Listeners := this.WindowMessageListeners[Message]
;If this instance isn't already registered for this message, increase listener count for this message
if(!Listeners.Listeners.HasKey(hwnd))
Listeners.ListenerCount++
;Register the message in the listeners list of the CWindowMessageHandler object associated with the current Message
Listeners.Listeners[hwnd] := FunctionName
}
CGUI_Assert(IsFunc(GUI[FunctionName]), "Invalid function definition " GUI[FunctionName] ". Function takes 3 parameters, Msg, wParam and lParam.")
}
UnregisterListener(hwnd, Message = "")
{
;Don't allow calling this function on the contained instances
if(this.Base.__Class = this.__Class)
return
GUI := CGUI.GUIFromHWND(hwnd)
if(GUI)
{
;Remove one or all registered listeners associated with the window handle
if(!Message)
{
Messages := []
for Msg, Handler in this.WindowMessageListeners
Messages.Insert(MSG)
}
Else
Messages := [Message]
for index, CurrentMessage in Messages ;Process all messages that are affected
{
;Make sure the window is actually registered right now so it doesn't get unregistered multiple times if this function happens to be called more than once with the same parameters
Listeners := this.WindowMessageListeners
if(Listeners.HasKey(CurrentMessage) && Listeners[CurrentMessage].Listeners.HasKey(hwnd))
{
Listeners := Listeners[CurrentMessage]
;Remove this window from the listener array
Listeners.Listeners.Remove(hwnd, "")
;Decrease count of window class instances that listen to this message
Listeners.ListenerCount --
;If no more instances listening to a window message, remove the CWindowMessageHandler object from WindowMessageListeners and deactivate the OnMessage callback for the current message
if(Listeners.ListenerCount = 0)
{
this.WindowMessageListeners.Remove(CurrentMessage, "")
OnMessage(CurrentMessage, "")
}
}
}
}
}
}
__Delete()
{
}
/*
Function: OnMessage
Registers a window instance as a listener for a specific window message.
Parameters:
Message - The number of the window message
FunctionName - The name of the function contained in the instance of the window class that will be called when the message is received.
To stop listening, skip this parameter or leave it empty. To change to another function, simply specify another name (stopping first isn't required). The function won't be called anymore after the window is destroyed. DON'T USE GUI, DESTROY ON ANY WINDOWS CREATED WITH THIS LIBRARY THOUGH. Instaed use window.Destroy() or window.Close() when window.DestroyOnClose is enabled.
The function accepts four parameters, Message, wParam, lParam and hwnd (in this order). hwnd can be the window handle of the window or the related control (e.g. for WM_KEYDOWN)
*/
OnMessage(Message, FunctionName = "")
{
if(this.IsDestroyed)
return
if(FunctionName)
this.WindowMessageHandler.RegisterListener(Message, this.hwnd, FunctionName)
else
this.WindowMessageHandler.UnregisterListener(this.hwnd, Message)
}
/*
Function: Destroy
Destroys the window. Any possible references to this class should be removed so its __Delete() function may get called. Make sure not attempt to use this window anymore!
*/
Destroy()
{
if(this.IsDestroyed)
return
;Remove it from GUI list
CGUI.GUIList.Remove(this.GUINum) ;make sure not to alter other GUIs here
this.IsDestroyed := true
this.WindowMessageHandler.UnregisterListener(this.hwnd) ;Unregister all registered window message listener functions
;Destroy the GUI
Gui, % this.GUINum ":Destroy"
;Call PostDestroy function
if(IsFunc(this.PostDestroy))
{
this.PostDestroy()
this.OnDestroy.(this)
}
}
/*
Function: Show
Shows the window.
Parameters:
Options - Same as in Gui, Show command
*/
Show(Options="")
{
if(this.IsDestroyed)
return
if(this.HasKey("_Initialized")) ;Prevent recalculating size after it has been shown the first time
this.Remove("_Initiliazed")
Gui, % this.GUINum ":Show",%Options%, % this.Title
}
/*
Function: Activate
Activates the window.
*/
Activate()
{
if(this.IsDestroyed)
return
WinActivate, % "ahk_id " this.hwnd
}
/*
Function: Close
Closes the window. Effectively the same as clicking the x in the title bar.
*/
Close()
{
PostMessage, 0x112, 0xF060,,, % "ahk_id " this.hwnd ; 0x112 = WM_SYSCOMMAND, 0xF060 = SC_CLOSE
}
/*
Function: Hide
Hides the window.
*/
Hide()
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Hide"
}
/*
Function: Minimize
Minimizese the window.
*/
Minimize()
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Minimize"
}
/*
Function: Maximize
Maximizes the window.
*/
Maximize()
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Maximize"
}
/*
Function: Restore
Restores the window.
*/
Restore()
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Restore"
}
/*
Function: Redraw
Attempts to redraw the window.
*/
Redraw()
{
if(this.IsDestroyed)
return
WinSet, Redraw,,% "ahk_id " this.hwnd
}
/*
Disable this stuff
a Function: Font
Changes the default font used for controls from here on.
a Parameters:
Options - Font options, size etc. See http://www.autohotkey.com/docs/commands/Gui.htm#Font
Fontname - Name of the font. See http://www.autohotkey.com/docs/commands/Gui.htm#Font
*/
;~ Font(Options, Fontname)
;~ {
;~ if(this.IsDestroyed)
;~ return
;~ Gui, % this.GUINum ":Font", %Options%, %Fontname%
;~ }
/*
Function: Color
Changes the default font used for controls from here on.
Parameters:
WindowColor - Color of the window background. See http://www.autohotkey.com/docs/commands/Gui.htm#Color
ControlColor - Color for controls. See http://www.autohotkey.com/docs/commands/Gui.htm#Color
*/
Color(WindowColor, ControlColor)
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Color", %WindowColor%, %ControlColor%
}
/*
Function: Margin
Changes the margin used between controls. Previously added controls are not affected.
Parameters:
x - Distance between controls on the x-axis.
y - Distance between controls on the y-axis.
*/
Margin(x, y)
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Margin", %x%, %y%
}
/*
Function: Flash
Flashes the taskbar button of this window.
Parameters:
Off - Leave empty to flash the taskbar. Use "Off" to disable flashing and restore normal state.
*/
Flash(Off = "")
{
if(this.IsDestroyed)
return
Gui, % this.GUINum ":Flash", %Off%
}
/*
Function: AddMenuBar
Attaches a menu bar to the window. The menu object can be accessed under the "Menu" property of the class instance deriving from CGUI.
Parameters:
Menu - An instance of <CMenu> containing the menu information. Leave empty to remove the menu. If the menu object is attached as menu bar, it can not be used as a context menu anymore.
*/
AddMenuBar(Menu)
{
if(this.IsDestroyed)
return
this.Menu := Menu
Gui, % this.GUINum ":Menu", % Menu.Name
}
/*
Function: ShowMenu
Shows a menu. This function is the preferred way to show menus that use callback functions inside the gui class that derives from CGUI.
Paramters:
Menu - The <CMenu> object which contains the menu information
X - X position of the menu.
Y - Y position of the menu.
*/
ShowMenu(Menu, X="", Y="")
{
Menu.Show(this.GUINum, X, Y)
}
/*
Function: AddControl
Creates and adds a control to the window.
Parameters:
Control - The type of the control. The control needs to be a name that can be translated to a class inheriting from CControl, e.g. "Text" -> "CTextControl". Valid values are:
- Text
- Edit
- Button
- Checkbox
- Radio
- ListView
- ComboBox
- DropDownList
- ListBox
- TreeView
- Tab
- GroupBox
- Picture
- Progress
- ActiveXControl
- SysLinkControl
Name - The name of the control. Names must be unique and must not be empty. The returned control object is usually assigned to GUI.ControlName after calling this function and can then be accessed by its name directly from the GUI object, i.e. GUI.MyEdit1 or similar. Otherwise the control can be accessed by window handle through the GUI.Controls array.
Options - Default options to be used for the control. These are in default AHK syntax according to <http://www.autohotkey.com/docs/commands/Gui.htm#OtherOptions> and <http://www.autohotkey.com/docs/commands/GuiControls.htm>. Do not use GUI variables (v option) and g-labels (g option).
Text - Text of the control. For some controls, this parameter has a special meaning. It can be a list of items or a collection of column headers separated by "|".
Returns: The created control object
*/
AddControl(Control, Name, Options = "", Text = "", ControlList="", ParentControl = "")
{
local hControl, type, testHWND, vName, NeedsGLabel
if(this.IsDestroyed)
return
;Validate name.
if(!CGUI_Assert(Name, "GUI.AddControl() : No name specified. Please supply a proper control name.", -2))
return
;Make sure not to add a control with duplicate name.
if(!CGUI_Assert(!IsObject(ControlList) || !IsObject(ControlList[Name]), "GUI.AddControl(): The control " Name " already exists. Please choose another name!", -2))
return
type := Control
if (CGUI_Assert(CGUI.RegisteredControls.hasKey(Control), "The control " Control " was not found!", -2))
{
Control := {base: CGUI.RegisteredControls[Control]}
Control.__Init()
if (Control.__New.MinParams > 4)
hControl := Control.__New(Name, Options, Text, this.GUINum, type)
else
hControl := Control.__New(Name, Options, Text, this.GUINum)
if(!CGUI_Assert(hControl != 0, "Error creating " Type "Control", -2))
return
}
;The __New constructor of a control may return a window handle when it needs to create the control manually.
if(!hControl)
{
;Old: (IsLabel(this.__Class "_" Control.Name) ? "g" this.__Class "_" Control.Name : "")
NeedsGLabel := Control._.HasKey("Events")
;Find a free GUI variable. Might need to add an index because there might be controls with the same name that are nested in other controls
GuiControlGet, testHWND, % this.GUINum ":hwnd", % vName := this.GUINum "_" Control.Name
while(testHWND)
GuiControlGet, testHWND, % this.GUINum ":hwnd", % vName := this.GUINum "_" Control.Name A_Index
;If the control that is added here is a subcontrol of a control in a tab control, we need to set the corresponding tab first and unset it after the control has been added
if(IsObject(ParentControl) && IsObject(this.Controls[ParentControl.hParentControl]) && this.Controls[ParentControl.hParentControl].Type = "Tab")
Gui, % this.GUINum ":Tab", % ParentControl.TabNumber, % this.Controls[ParentControl.hParentControl]._.TabIndex
Gui, % this.GUINum ":Add", % Control.Type, % Control.Options " hwndhControl " (NeedsGLabel ? "gCGUI_HandleEvent " : "") "v" vName, % Control.Content ;Create the control and get its window handle and setup a g-label
if(IsObject(ParentControl) && IsObject(this.Controls[ParentControl.hParentControl]) && this.Controls[ParentControl.hParentControl].Type = "Tab")
Gui, % this.GUINum ":Tab"
}
Control.Insert("hwnd", hControl) ;Window handle is used for all further operations on this control
;Call PostCreate to allow the control to do things with itself after it was created. CControl.PostCreate needs to be called anyway, so all controls that handle PostCreate need to add this.base.PostCreate().
Control.PostCreate()
Control.Remove("Content")
if(ControlList)
ControlList[Control.Name] := Control
this.Controls[hControl] := Control ;Add to list of controls
;This will make sure that the window updates its size each time a new control is added until it is shown for the first time
if(this.HasKey("_Initialized") && !this.Visible)
Gui, % this.GUINum ":Show", % "Hide Autosize" (this._Initialized ? "" : " Center") ;If _Initialized is true the position was changed manually and mustn't be updated
return Control
}
/*
Function: Validate
Validates the contents of controls containing text fields by calling <Control.Validate> for each fitting control.
The control can then make adjustments to the passed value and return an adjusted or same value which is then used as its text.
*/
Validate()
{
for hwnd, Control in this.Controls
if(Control.IsValidatableControlType())
Control.Validate()
}
/*
Function: ControlFromHWND
Returns the object that belongs to a control with a specific window handle.
Parameters:
HWND - The window handle.
*/
ControlFromHWND(hwnd)
{
for GUINum, GUI in this.GUIList
if(GUI.Controls.HasKey(hwnd))
return GUI.Controls[hwnd]
}
/*
Function: GUIFromHWND
Returns the GUI object with a specific hwnd
*/
GUIFromHWND(hwnd)
{
for GUINum, GUI in this.GUIList
if(GUI.hwnd = hwnd)
return GUI
}
/*
Function: ControlFromGUINumAndName
Returns the object that belongs to a window with a specific gui number and a control with a specific name.
Parameters:
GUINum - The GUI number
*/
ControlFromGUINumAndName(GUINum, Name)
{
return this.GUIList[GUINum][Name]
}
/*
Function: RegisterControl
Registers a new control for use with <AddControl>.
This is typically called from static variable initializations to make the control available to the user on startup.
Parameters:
name - the name the user can use to create a control, such as "Text"
class - the class object implementing the control, such as CTextControl.
*/
RegisterControl(name, class)
{
this.RegisteredControls[name] := class
}
/*
Property: RegisteredControls
This static object contains a mapping of allowed control names and the responsible classes.
It is used internally to register new controls for use with <AddControl>.
Property: GUIList
This static array contains a list of all GUIs created with this library.
It is maintained automatically and should not need to be used directly.
Property: IsDestroyed
True if the window has been destroyed and this object is not usable anymore.
Property: X
x-Position of the window (including its border) in screen coordinates.
Property: Y
y-Position of the window (including its border) in screen coordinates.
Property: Width
Width of the client area of the window.
Property: Height
Height of the client area of the window.
Property: WindowWidth
Width of the window.
Property: WindowHeight
Height of the window.
Property: Position
An object containing the X and Y properties. They can not be set separately through this object, only both at once.
Property: Size
An object containing the Width and Height values. They can not be set separately through this object, only both at once.
Property: WindowSize
An object containing the WindowWidth and WindowHeight values. They can not be set separately through this object, only both at once.
Property: Title
The window title.
Property: Style
The window style.
Property: ExStyle
The window extended style.
Property: Delimiter
The delimiter used (mostly internally) for multiple items in a control. The default value is "|", but you can change it to something like "`n" or "`t" if you need to use "|" in the control text.
Property: Transcolor
A color that will be made invisible/see-through on the window. Values: RGB|ColorName|Off
Property: Transparent
The window style.
Property: MinMax
The window state: -1: minimized / 1: maximized / 0: neither. Can not be set this way.
Property: ActiveControl
The control object that is focused. Can also be set.
Property: Enabled
If false, the user can not interact with this window. Used for creating modal windows.
Property: Visible
Sets wether the window is visible or hidden.
Property: AlwaysOnTop
If true, the window will be in front of other windows.
Property: Border
Provides a thin border around the window.
Property: Caption
Set to false to remove the title bar.
Property: MinimizeBox
Determines if the window has a minimize button in the title bar.
Property: MaximizeBox
Determines if the window has a maximize button in the title bar.
Property: Resize
Determines if the window can be resized by the user.
Property: SysMenu
If true, the window will show a it's program icon in the title bar and show its system menu there.
Property: Instances
A list of all instances of the current window class. If you inherit from CGUI you can use this to find all windows of this type.
Property: Menu
If this variable contains an instance of <CMenu> and there is no ContextMenu() event handler for this window, this menu will be shown when the user right clicks on an empty window area.
Property: MinSize
Minimum window size when Resize is enabled.
Property: MaxSize
Maximum window size when Resize is enabled.
Property: Theme
Property: Toolwindow
Provides a thin title bar.
Property: Owner
Assigning a hwnd to this property makes this window owned by another so it will act like a tool window of said window. Supports any window, not just windows from this process.
Property: OwnerAutoClose
By enabling this, an owned window (which has its Owner property set to the window handle of its parent window) will automatically close itself when its parent window closes.
The window can use its PreClose() event to decide if it should really be closed, but the owner status will be removed anyway.
To archive this behaviour a shell message hook is used. If there is already such a hook present in the script, this library will intercept it and forward any messages to the original callback function.
Property: OwnDialogs
Determines if the dialogs that this window shows will be owned by it.
Property: Region
Property: WindowColor
Property: ControlColor
Property: DestroyOnClose
If set, the window will be destroyed when it gets closed.
Property: CloseOnEscape
If set, the window will close itself when escape is pressed.
Property: ValidateOnFocusLeave
If set, <CGUI.Validate> is called each time a text-containing variable loses focus.
*/
__Get(Name)
{
if Name not in base,_,GUINum
{
DetectHidden := A_DetectHiddenWindows
DetectHiddenWindows, On
if(Name = "IsDestroyed" && this.GUINum) ;Extra check in __Get for this property because it might be destroyed through an old-style Gui, Destroy command
{
GUI, % this.GUINum ":+LastFoundExist"
Value := WinExist() = 0
}
else if(Name != "IsDestroyed" && !this.IsDestroyed)
{
if Name in width,height
{
VarSetCapacity(rc, 16)
DllCall("GetClientRect", "PTR", this.hwnd, "PTR", &rc, "UINT")
Value := NumGet(rc, {x : 0, y : 4, width : 8, height : 12}[Name], "int")
}
else if Name in X,Y
{
WinGetPos, X, Y, , , % "ahk_id " this.hwnd
return Name = "X" ? X : Y
}
else if(Name = "Position")
{
WinGetPos, X, Y, , , % "ahk_id " this.hwnd
Value := {x : X, y : Y}
}
else if Name in WindowWidth,WindowHeight
{
WinGetPos, , , Width, Height, % "ahk_id " this.hwnd
return Name = "WindowWidth" ? Width : Height
}
else if(Name = "Size")
{
VarSetCapacity(rc, 16)
DllCall("GetClientRect", "PTR", this.hwnd, "PTRP", rc, "UINT")
Value := {width : NumGet(rc, 8, "int"), height : NumGet(rc, 12, "int")}
}
else if(Name = "WindowSize")
{
WinGetPos, , , Width, Height, % "ahk_id " this.hwnd
return {Width : Width, Height : Height}
}
else if(Name = "Title")
WinGetTitle, Value, % "ahk_id " this.hwnd
else if Name in Style,ExStyle,TransColor,Transparent,MinMax
WinGet, Value, %Name%, % "ahk_id " this.hwnd
else if(Name = "ActiveControl") ;Returns the control object that has keyboard focus
{
ControlGetFocus, Value, % "ahk_id " this.hwnd
ControlGet, Value, Hwnd,, %Value%, % "ahk_id " this.hwnd
if(this.Controls.HasKey(Value))
Value := this.Controls[Value]
}
else if(Name="Enabled")
Value := !(this.Style & 0x8000000) ;WS_DISABLED
else if(Name = "Visible")
Value := this.Style & 0x10000000
else if(Name = "AlwaysOnTop")
Value := this.ExStyle & 0x8
else if(Name = "Border")
Value := this.Style & 0x800000
else if(Name = "Caption")
Value := this.Style & 0xC00000
else if(Name = "MaximizeBox")
Value := this.Style & 0x10000
else if(Name = "MinimizeBox")
Value := this.Style & 0x10000
else if(Name = "Resize")
Value := this.Style & 0x40000
else if(Name = "SysMenu")
Value := this.Style & 0x80000
}
if(Value = "" && Name = "Instances") ;Returns a list of instances of this window class
{
Value := []
for GuiNum,GUI in CGUI.GUIList
if(GUI.__Class = this.__Class)
Value.Insert(GUI)
}
else if(Value = "" && CGUI_IndexOf(["Delimiter", "MinSize", "MaxSize", "Theme", "ToolWindow", "Owner", "OwnDialogs", "Region", "WindowColor", "ControlColor", "ValidateOnFocusLeave"], Name))
Value := this._[Name]
if(!DetectHidden)
DetectHiddenWindows, Off
if(Value != "")
return Value
}
}
__Set(Name, Params*)
{
DetectHidden := A_DetectHiddenWindows
DetectHiddenWindows, On
Handled := true
if(!this.IsDestroyed)
{
;Fix completely weird __Set behavior. If one tries to assign a value to a sub item, it doesn't call __Get for each sub item but __Set with the subitems as parameters.
Value := Params.Remove()
if(Params.MaxIndex())
{
Params.Insert(1, Name)
Name := Params.Remove()
return (this[Params*])[Name] := Value
}
if Name in AlwaysOnTop,Border,Caption,LastFound,LastFoundExist,MaximizeBox,MaximizeBox,MinimizeBox,Resize,SysMenu
Gui, % this.GUINum ":" (Value = 1 ? "+" : "-") Name
else if Name in OwnDialogs,Theme,ToolWindow
{
Gui, % this.GUINum ":" (Value = 1 ? "+" : "-") Name
this._[Name] := Value = 1
}
else if Name in MinSize,MaxSize
{
Gui, % this.GUINum ":+" Name Value
if(!IsObject(this._[Name]))
this._[Name] := {}
Loop, Parse, Value, x
{
if(!A_LoopField)
this._[Name][A_Index = 1 ? "width" : "height"] := A_Index = 1 ? this.width : this.height
else
this._[Name][A_Index = 1 ? "width" : "height"] := A_LoopField
}
}
else if(Name = "Delimiter")
{
Gui, % this.GUINum ":+Delimiter" Value
this._.Delimiter := Value
}
else if(Name = "Owner")
{
if(Value && WinExist("ahk_id " Value))
{
DllCall("SetWindowLong" (A_PtrSize = 4 ? "" : "Ptr"), "Ptr", this.hwnd, "int", -8, "PTR", Value) ;This line actually sets the owner behavior
this._.hOwner := Value
}
else
{
DllCall("SetWindowLong" (A_PtrSize = 4 ? "" : "Ptr"), "Ptr", this.hwnd, "int", -8, "PTR", 0) ;Remove tool window behavior
this._.Remove("hOwner")
}
}
else if(Name = "OwnerAutoClose" && this._.HasKey("hOwner"))
{
if(Value = 1)
{
if(!CGUI._.ShelllHook)
{
DllCall( "RegisterShellHookWindow", "Ptr", A_ScriptHWND)
CGUI._.ShellHookMsg := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
CGUI._.ShellHook := OnMessage(CGUI._.ShellHookMsg, "CGUI_ShellMessage")
if(CGUI._.ShellHook = "CGUI_ShellMessage")
CGUI._.ShellHook := 1
}
this._.OwnerAutoClose := 1
}
else
{
if(this._.OwnerAutoClose)
{
for GUINum, GUI in CGUI.GUIList
if(GUI.hwnd != this.hwnd && GUI._.OwnerAutoClose)
found := true
if(!found)
{
OnMessage(CGUI._.ShellHookMsg, (CGUI._.ShellHook && CGUI._.ShellHook != 1) ? CGUI._.ShellHook : "")
if(!CGUI._.ShellHook)
DllCall("DeRegisterShellHookWindow", "Ptr", A_ScriptHWND)
CGUI._.Remove("ShellHook")
}
}
this._.OwnerAutoClose := 0
}
}
else if Name in Style,ExStyle,Transparent,TransColor
WinSet, %Name%, %Value%, % "ahk_id " this.hwnd
else if(Name = "Region")
{
WinSet, Region, %Value%, % "ahk_id " this.hwnd
this._.Region := Value
}
else if Name in x,y,WindowWidth,WindowHeight
{
WinMove,% "ahk_id " this.hwnd,,% Name = "x" ? Value : "", % Name = "y" ? Value : "", % Name = "WindowWidth" ? Value : "", % Name = "WindowHeight" ? Value : ""
if(Name = "WindowWidth" || Name = "WindowHeight") ;Prevent recalculating size after it has been set manually
this.Remove("_Initialized")
else if(this.HasKey("_Initialized")) ;Mark that position was changed manually during recalculation phase
this._Initialized := true
}
else if(Name = "Position")
{
WinMove,% "ahk_id " this.hwnd,,% Value.x, % Value.y
if(this.HasKey("_Initialized")) ;Mark that position was changed manually during recalculation phase
this._Initialized := true
}
else if(Name = "Size")
{
Size := this.Size
WinSize := this.WindowSize
WinMove, % "ahk_id " this.hwnd,,,, % Value.width + WinSize.Width - Size.Width, % Value.height + WinSize.Height - Size.Height
this.Remove("_Initialized")
}
else if(Name = "WindowSize")
{
WinMove, % "ahk_id " this.hwnd,,,, % Value.width, % Value.height
this.Remove("_Initialized")
}
else if(Name = "Title")
WinSetTitle, % "ahk_id " this.hwnd,,%Value%
else if(Name = "WindowColor")
Gui, % this.GUINum ":Color", %Value%
else if(Name = "ControlColor")
Gui, % this.GUINum ":Color",, %Value%
else if(Name = "ActiveControl")
{
if(!IsObject(Value) && WinExist("ahk_id " Value))
Value := this.Controls[Value]
else if(!IsObject(Value))
Value := this[Value]
if(IsObject(Value))
ControlFocus,,% "ahk_id " Value.hwnd
}
else if(Name = "Enabled")
this.Style := (Value ? "-" : "+") 0x8000000 ;WS_DISABLED
else if(Name = "Visible")
{
this.Style := (Value ? "+" : "-") 0x10000000 ;WS_VISIBLE
if(Value) ;Prevent recalculating size after it has been shown the first time
this.Remove("_Initialized")
}
else if(Name = "ValidateOnFocusLeave")
this._[Name] := Value = 1
else if(Name = "_Constructor") ;_Constructor is just a temporary variable name for automatically calling the CGUI constructor
Handled := true
else if(Name = "_") ;Prohibit setting the proxy object
Handled := true
else
Handled := false
}
else
Handled := false
if(!DetectHidden)
DetectHiddenWindows, Off
if(Handled)
return Value
}
/*
Event: Introduction
Events are used by implementing the specific event function in the class that extends CGUI. No g-labels are required nor anything else.
On a related note, you can listen to window messages by calling <CGUI.OnMessage> on a window instance.
Event: ContextMenu()
Invoked when the user right clicks on a an empty area in this window. It is possible to show a static context menu without handling this event by assigning the Menu property of this window to an instance of <CMenu>.
Event: DropFiles()
Invoked when the user dropped files on the window.
Event: Escape()
Invoked when the user pressed Escape. Having the window close itself when escape gets pressed can be easily done by setting CloseOnEscape := 1 and does not need this event.
Event: PreClose()
Invoked when the window is about to close. This function can stop the closing of the window by returning true. Otherwise the window will be destroyed or hidden, depending on the setting of DestroyOnClose.
Event: PostDestroy()
Invoked when the window was destroyed. It's not possible to interact with the window or its controls anymore so this event should only be used to free possible resources.
Event: Size(Event)
Invoked when the window gets resized.
0: The window has been restored, or resized normally such as by dragging its edges.
1: The window has been minimized.
2: The window has been maximized.
*/