Simple GDI Class

Post your working scripts, libraries and tools for AHK v1.1 and older
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Simple GDI Class

05 Jan 2015, 17:14

A class aiming to make using low-level GDI functions simple.

Example use code: Simple button
Image

Code: Select all

#Include GDI.ahk

Gui, Margin, 50, 50
Gui, Add, Progress, w100 h50 hWndhWnd ; Progress controls make ideal canvases
Gui, Show

global MyButton := new CustomButton(hWnd)
OnMessage(0xF, "WM_PAINT")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x201, "WM_LBUTTONDOWN")
OnMessage(0x202, "WM_LBUTTONUP")
return

GuiClose:
ExitApp

WM_PAINT()
{
	Sleep, -1 ; Let the scrollbar redraw before painting over it
	MyButton.BitBlt()
}

WM_MOUSEMOVE(wParam, lParam, Msg, hWnd)
{
	static LasthWnd
	if (hWnd != LasthWnd)
	{
		if (hWnd == MyButton.hWnd)
			MyButton.Hovering()
		else if (lasthWnd == MyButton.hWnd)
			MyButton.Default()
		LasthWnd := hWnd
	}
}

WM_LBUTTONDOWN(wParam, lParam, Msg, hWnd)
{
	if (hWnd == MyButton.hWnd)
		MyButton.Held()
}

WM_LBUTTONUP(wParam, lParam, Msg, hWnd)
{
	if (hWnd == MyButton.hWnd)
		MyButton.Hovering()
}

class CustomButton
{
	__New(hWnd)
	{
		this.GDI := new GDI(hWnd)
		this.hWnd := hWnd
		this.Default()
	}
	
	Default()
	{
		this.Draw("Hover", 0xFFFFFF, 0x000000)
	}
	
	Hovering()
	{
		this.Draw("Click", 0xC0C0C0, 0x000000)
	}
	
	Held()
	{
		this.Draw("Release", 0x000000, 0x0000FF)
	}
	
	Draw(Text, Color, TextColor)
	{
		critical
		this.GDI.FillRectangle(0, 0, this.GDI.CliWidth, this.GDI.CliHeight, Color, TextColor)
		this.GDI.DrawText(0, 0, 100, 50, Text, TextColor, "Courier New", 20, "CC")
		this.GDI.BitBlt()
	}
	
	BitBlt()
	{
		this.GDI.BitBlt()
	}
}
The library code:
Last edited by geek on 05 Jan 2015, 17:41, edited 3 times in total.
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Simple GDI Class

05 Jan 2015, 17:24

Nice!
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
Soft
Posts: 174
Joined: 07 Jan 2015, 13:18
Location: Seoul
Contact:

Re: Simple GDI Class

07 Jan 2015, 22:56

oh.. nice
AutoHotkey & AutoHotkey_H v1.1.22.07
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: Simple GDI Class

07 Jan 2015, 22:57

"oh.."?
vasili111
Posts: 747
Joined: 21 Jan 2014, 02:04
Location: Georgia

Re: Simple GDI Class

18 Jan 2015, 11:43

DRAKON-AutoHotkey: Visual programming for AutoHotkey.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: Simple GDI Class

18 Jan 2015, 12:49

It's not really beautiful, but I'll post it anyways if you want me to
User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Simple GDI Class

19 Jan 2015, 00:00

But! It will facilitate new GUIs using GDI
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
lblb
Posts: 190
Joined: 30 Sep 2013, 11:31

Re: Simple GDI Class

14 Apr 2016, 23:29

Hi Geekdude,

This is cool and simple! Thanks for your work.

Is there any way that this could be adapted to make circular buttons? There are ellipse functions in Tic's Gdip library so I'm guessing it may be possible, but adding it to your class is way over my head. Thanks in advance for the help you may be able to provide.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: Simple GDI Class

14 Apr 2016, 23:39

Greetings lblb,

You should be able to take the code for FillRectangle and modify it to use a DllCall for Ellipse instead of for Rectangle. It looks like it takes all the same parameters as Rectangle, so it should be as simple as copying that class method and changing the name Rectangle to Ellipse. I'd probably also remove that 1x1 pixel check, since it doesn't really apply to an ellipse in the same way it might a rectangle.

Here is the documentation page for Ellipse
lblb
Posts: 190
Joined: 30 Sep 2013, 11:31

Re: Simple GDI Class

14 Apr 2016, 23:52

Hi Geekdude,

Thanks a lot for the quick reply and for the info, much appreciated.

I'm willing to work on it but this is quite new to me. So before I invest some time into this, maybe I should ask if you think it would be possible to reach my objective. What I am trying to do is to have individual floating circle buttons that react as you showed in your test script. So I would make the Gui background transparent in order to only see the circle buttons (and have some free space between the buttons). If I can come up with the Ellipse code, I'm not sure what to expect: since the Fill functions seem to act by changing the color of the progress control, would I be stuck with the corner of the rectangular progress control sticking out of the ellipse shape?
lblb
Posts: 190
Joined: 30 Sep 2013, 11:31

Re: Simple GDI Class

15 Apr 2016, 03:11

Hi Geekdude,

Thanks a lot for your help. I think I'm getting close to what I need. Here is the code I have so far:
Spoiler
And then in GDI.ahk, I replaced FillRectangle with:
Spoiler
When you run the script above, there should be two round buttons at the middle of the screen.

A few questions:


1) I made the default state of the buttons transparent by using

Code: Select all

Winset, Transcolor, 0x000000
It was necessary to use 0x000000 for Transcolor (and for the Gui background color) because the corners of the progress control that are outside of the ellipse are black. Is there any way to control the color of these corners outside of the ellipse?


2) As you can see, the edges of the circle and of the text are a bit jagged. In Tic's Gdip library, it's possible to adjust the antialiasing using the Quality functions. Would it be possible to do the same here? I have no clue how I could include this function in your code.
Spoiler

3) I'm trying to put different text on each button. That works well by itself using something like:

Code: Select all

Default()
	{
	global LasthWnd
	global hwnd
	if (hWnd == MyButton.hWnd)
			this.Draw("Hover1", 0x000000, 0x00000F)
	Else if (hWnd == MyButton2.hWnd)
			this.Draw("Hover2", 0x000000, 0x00000F)
	}
But I'm running in some issues when I combine this with the function to reset the buttons when the cursor leaves the buttons. When the cursor leaves the the buttons, I use the following to reset the buttons:

Code: Select all

OnMessage(0x2A2, "WM_NCMOUSELEAVE")
SetTimer, CheckIfMouseIsOutsideGui,10
If you hover on and off over button 1 (or button 2), the text is updated correctly. But if you hover over button 1, then go over button 2, the first time you hover over button 2 the hover text is not updated correctly. Can you see where the mistake is?


Thanks a lot for all your help. This is really challenging for me as I'm not used to working with functions and classes (so for example, I'm not really sure the WM_NCMOUSELEAVE and CheckIfMouseIsOutsideGui are deployed correctly) but I'm learning a whole lot. So thanks in advance for any support you may be able to offer.
Guest

Re: Simple GDI Class

19 Apr 2016, 09:34

I'm running into some issues with multiple buttons too...
It seems they get switched around for some reason. Can't pinpoint why yet.
There seems to be some HWND weirdness going on.

Could you provide an example of using more than one button Geekdude? Thanks.
Like this it seems kinda confusing to get things right. (I usually don't work with HWNDS to get things done)
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: Simple GDI Class

20 Apr 2016, 12:16

Sorry for the late replies. Here's some code with multiple round buttons. I took advantage of some newer AHK features that let me handle the messages for each button individually instead of having a single message handler that handles all the buttons. Also, I fixed WM_MOUSELEAVE (had to do some research for this one, and modified it to be both 32 and 64 bit compatible). See the original post for the updated GDI.ahk with FillEllipse.

As for making things smoother/less pixelated, it's not really possible. My class strictly uses GDI (not plus) functions, which don't have such features. That kind of functionality is only present in GDI+. If you want good transparency and aliasing (smoothing), you'll need to use either tic's gdip or his gdip2 beta (also class based). https://github.com/tariqporter/Gdip/blo ... /Gdip2.ahk

Code: Select all

#NoEnv
SetBatchLines, -1

#Include <GDI>

; Window Message Constants
global WM_PAINT := 0xF
, WM_MOUSEMOVE := 0x200
, WM_LBUTTONDOWN := 0x201
, WM_LBUTTONUP := 0x202
, WM_MOUSELEAVE := 0x2A3

; TRACKMOUSEEVENT struct for WM_MOUSELEAVE
; https://autohotkey.com/board/topic/32663-/?p=207708
VarSetCapacity(TME, 24, 0)
NumPut(8+A_PtrSize*2, TME, 0, "UInt") ; cbSize
NumPut(2, TME, 4, "UInt") ; dwFlags
NumPut(0, TME, 8, "UPtr") ; hwndTrack
NumPut(0, TME, 8+A_PtrSize, "UInt") ; dwHoverTime

Buttons := []
Loop, 5
{
	Gui, Add, Progress, w150 h100 hWndhWnd
	Buttons.Push(new CustomButton(hWnd))
}
Gui, Show
return

GuiClose:
Gui, Destroy

; Erase all button references so the class instances can be deleted
for Index, Button in Buttons
	Button.Release()
Button := "" ; "Button" from the for loop actually still exists, so delete that reference too
Buttons := ""

ExitApp
return

class CustomButton
{
	Messages := [WM_PAINT, WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSELEAVE]
	
	__New(hWnd)
	{
		this.hWnd := hWnd
		this.GDI := new GDI(hWnd)
		
		; Fill in background
		this.GDI.FillRectangle(0, 0, this.GDI.CliWidth, this.GDI.CliHeight, 0xF0F0F0)
		
		; Fill in foreground
		this.Default()
		
		; Listen for messages
		this.Bound := this.OnMessage.Bind(this)
		for Index, Msg in this.Messages
			OnMessage(Msg, this.Bound)
	}
	
	Release()
	{
		for Index, Msg in this.Messages
			OnMessage(Msg, this.Bound, 0)
		this.Bound := "" ; Erase circular reference
	}
	
	OnMessage(wParam, lParam, Msg, hWnd)
	{
		global TME
		
		; If the/a window needs repainted
		if (Msg == WM_PAINT)
		{
			Sleep, -1
			this.BitBlt()
		}
		
		; From this point on, only handle messages specifically for this control
		if (hWnd != this.hWnd)
			return
		
		if (Msg == WM_MOUSEMOVE) {
			NumPut(hWnd, TME, 8, "Ptr") ; hwndTrack
			DllCall("TrackMouseEvent", "Ptr", &TME)
			
			; TODO: only call on state change
			if !GetKeyState("LButton")
				this.Hovering()
		} else if (Msg == WM_MOUSELEAVE)
			this.Default()
		else if (Msg == WM_LBUTTONDOWN)
			this.Held()
		else if (Msg == WM_LBUTTONUP)
			this.Hovering()
	}
	
	Default()
	{
		this.Draw("Hover", 0xFFFFFF, 0x000000)
	}
	
	Hovering()
	{
		this.Draw("Click", 0xC0C0C0, 0x000000)
	}
	
	Held()
	{
		this.Draw("Release", 0x000000, 0x0000FF)
	}
	
	Draw(Text, Color, TextColor)
	{
		this.GDI.FillEllipse(0, 0, this.GDI.CliWidth, this.GDI.CliHeight, Color, TextColor)
		this.GDI.DrawText(0, 0, this.GDI.CliWidth, this.GDI.CliHeight, Text, TextColor, "Comic Sans", 20, "CC")
		this.GDI.BitBlt()
	}
	
	BitBlt()
	{
		this.GDI.BitBlt()
	}
}
Guest

Re: Simple GDI Class

20 Apr 2016, 13:18

Cheers :beer:

I managed to get things looking smooth enough with your library so no need for gdip on my end.

Now if only i could find some bass.dll help i'd be set :lol: lol
lblb
Posts: 190
Joined: 30 Sep 2013, 11:31

Re: Simple GDI Class

25 Apr 2016, 01:53

Hi GeekDude,

Sorry for the late reply as I just got back into this. Thanks a lot for modifying the GDI.ahk and for modifying my script. I really appreciate it. I'll look into what you wrote and will write back here if I encounter any issues. Thanks a lot!

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 104 guests