前7章里,我们已经学会用绘画工具画出各种东西,也能保存起来,但却不能和它们有什么交互。
所以本章,我们会创建一个可以拖动的圆角矩形文字框。
1.使用定式直接创建出画布
#SingleInstance, Force
#NoEnv
SetBatchLines, -1
; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip_All.ahk
; 初始化 gdi+ 需要的一切
gosub, init
2.画圆角矩形
; Create a partially transparent, black brush (ARGB = Transparency, red, green, blue) to draw a rounded rectangle with
; Fill the graphics of the bitmap with a rounded rectangle using the brush created
; Filling the entire graphics - from coordinates (0, 0) the entire width and height
; The last parameter (20) is the radius of the circles used for the rounded corners
; Delete the brush as it is no longer needed and wastes memory
; 创建画刷
pBrush := Gdip_BrushCreateSolid(0xaa000000)
; 填充圆角矩形
; 特别注意,要想画出完美的圆角矩形,需要提前做 Gdip_SetPixelOffsetMode(G, 2) 这个设置。
; 我已经在 init 阶段就做了设置,故这里直接画了。
; Gdip_FillRoundedRectangle(画布, 画刷, x, y, 矩形宽, 矩形高, 圆角)
Gdip_FillRoundedRectangle(G, pBrush, 0, 0, Width, Height, 20)
Gdip_DeleteBrush(pBrush)
3.写字
; We can specify the font to use. Here we use Arial as most systems should have this installed
Font = 微软雅黑
; Next we can check that the user actually has the font that we wish them to use
; If they do not then we can do something about it. I choose to give a wraning and exit!
; 注意,这里仅仅检查是否存在字体而已,真正对字体的使用,是在后面的 Gdip_TextToGraphics() 中!
If !Gdip_FontFamilyCreate(Font)
{
MsgBox, 48, Font error!, The font you have specified does not exist on the system
ExitApp
}
; There are a lot of things to cover with the function Gdip_TextToGraphics
; The 1st parameter is the graphics we wish to use (our canvas)
; The 2nd parameter is the text we wish to write. It can include new lines `n
; The 3rd parameter, the options are where all the action takes place...
; You can write literal x and y coordinates such as x20 y50 which would place the text at that position in pixels
; or you can include the last 2 parameters (Width and Height of the Graphics we will use) and then you can use x10p
; which will place the text at 10% of the width and y30p which is 30% of the height
; The same percentage marker may be used for width and height also, so w80p makes the bounding box of the rectangle the text
; will be written to 80% of the width of the graphics. If either is missed (as I have missed height) then the height of the bounding
; box will be made to be the height of the graphics, so 100%
; Any of the following words may be used also: Regular,Bold,Italic,BoldItalic,Underline,Strikeout to perform their associated action
; To justify the text any of the following may be used: Near,Left,Centre,Center,Far,Right with different spelling of words for convenience
; The rendering hint (the quality of the antialiasing of the text) can be specified with r, whose values may be:
; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
; The size can simply be specified with s
; The colour and opacity can be specified for the text also by specifying the ARGB as demonstrated with other functions such as the brush
; So cffff0000 would make a fully opaque red brush, so it is: cARGB (the literal letter c, follwed by the ARGB)
; The 4th parameter is the name of the font you wish to use
; As mentioned previously, you don not need to specify the last 2 parameters, the width and height, unless
; you are planning on using the p option with the x,y,w,h to use the percentage
Options = x10p y30p w80p Centre cbbffffff r4 s20 Underline Italic
; Gdip_TextToGraphics(画布, 要显示的文字, 显示设置, 字体, 宽, 高)
; 第2个参数 要显示的文字 使用 `n 换行
; =====================================以下都是在说第3个参数======================================
; 第3个参数 显示设置 是此函数的精华,也是很复杂的一个参数。
; 文字位置可以使用 “x” “y” 来指定,例如 “x20 y50” 。
; 当设置了第5、6个参数时,还可以用百分比模式。例如 “x10p y30p” 则表示将文字放置在 x=10%宽 y=30%高 的位置。
; 同时 “w” “h” 也支持百分比模式。例如 “w80p” 表示文字显示的宽度范围是第5个参数的80%,也就是左右各留10%的空出来。
; 如果只指定了 “w” “h” 中的一个,那么另一个将是 100% 。例如只设置了 “w80p”,那么 h 会被自动设置为 “h100p” 。
; 字体属性(常规、粗体、斜体、粗斜体、下划线、删除线)可以用以下词来指定。
; Regular,Bold,Italic,BoldItalic,Underline,Strikeout
; 文字水平对齐方式(左对齐、左对齐、居中、居中、右对齐、右对齐)可以用以下词来指定。
; Near,Left,Centre,Center,Far,Right
; 文字纵向对齐方式(贴顶、贴顶、贴底、贴底、居中、居中)
; Top,Up,Bottom,Down,vCentre,vCenter
; 文字的抗锯齿效果可以用 r 来指定,例如 r0 。其值可以是以下这些(具体效果看图片演示吧)。
; SystemDefault = 0
; SingleBitPerPixelGridFit = 1
; SingleBitPerPixel = 2
; AntiAliasGridFit = 3
; AntiAlias = 4
; 字号可以用 s 来指定,例如 s14 。
; 颜色和透明度用 c 来指定,例如 cffff0000 。注意,这里 ARGB 格式的颜色。
; =====================================以上都是在说第3个参数=====================================
; 第4个参数 是字体名字
; 第5、6个参数可以省略,除非你在第3个参数指定 “文字位置” 时用到了百分比模式,那么就需要给第5、6个参数传值。否则不需要。
Gdip_TextToGraphics(G, "教程 8`n`nThank you for trying this example", Options, Font, Width, Height)
不同文字抗锯齿选项的效果
4.显示出来
; Update the specified window we have created (hwnd1) with a handle to our bitmap (hdc), specifying the x,y,w,h we want it positioned on our screen
; With some simple maths we can place the gui in the centre of our primary monitor horizontally and vertically at the specified heigth and width
; 显示出来
UpdateLayeredWindow(hwnd1, hdc, (A_ScreenWidth-Width)//2, (A_ScreenHeight-Height)//2, Width, Height)
5.实现拖动效果
; By placing this OnMessage here. The function WM_LBUTTONDOWN will be called every time the user left clicks on the gui
; 实现拖动效果
; 当我们的程序接收到 “0x201” 消息时,会自动跳到下面的 “WM_LBUTTONDOWN()” 中运行。
; 0x201 = WM_LBUTTONDOWN 即鼠标左键按下的消息。
; 再换一句话说就是,当程序发现自己被左键按下时,会自动跳到下面的 “WM_LBUTTONDOWN()” 中运行。
OnMessage(0x201, "WM_LBUTTONDOWN")
Return
;#######################################################################
; This function is called every time the user clicks on the gui
; The PostMessage will act on the last found window (this being the gui that launched the subroutine, hence the last parameter not being needed)
; 当左键在界面中按下时,发送消息 “0xA1 ,2” 即 “WM_NCLBUTTONDOWN , HTCAPTION”
; WM_NCLBUTTONDOWN , WM = 窗口消息(Window Message), NC = 非客户区(Non Client), 即标题栏或者边缘。 LBUTTONDOWN = 左键按下
; HTCAPTION = 命中标题栏
; 连起来就是,当鼠标左键在我们的自绘界面中被按下时,告诉系统,鼠标左键在标题栏被按下了,此时移动鼠标位置,自然就实现拖动效果了。
; 参考资料:
; https://stackoverflow.com/questions/8781862/what-does-wm-nclbuttondown-do
; https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-nclbuttondown?redirectedfrom=MSDN
; https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-nchittest
WM_LBUTTONDOWN()
{
PostMessage, 0xA1, 2
}
;#######################################################################
6.收工善后
ExitGdip:
; Select the object back into the hdc
SelectObject(hdc, obm)
; Now the bitmap may be deleted
DeleteObject(hbm)
; Also the device context related to the bitmap may be deleted
DeleteDC(hdc)
; The graphics may now be deleted
Gdip_DeleteGraphics(G)
; gdi+ may now be shutdown on exiting the program
Gdip_Shutdown(pToken)
ExitApp
Return
0.定式
init:
; Start gdi+
If !pToken := Gdip_Startup()
{
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
OnExit, ExitGdip
; Set the width and height we want as our drawing area, to draw everything in. This will be the dimensions of our bitmap
Width := 300, Height := 200
; Create a layered window (+E0x80000 : must be used for UpdateLayeredWindow to work!) that is always on top (+AlwaysOnTop), has no taskbar entry or caption
Gui, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
; Show the window
Gui, 1: Show, NA
; Get a handle to this window we have created in order to update it later
hwnd1 := WinExist()
; Create a gdi bitmap with width and height of what we are going to draw into it. This is the entire drawing area for everything
hbm := CreateDIBSection(Width, Height)
; Get a device context compatible with the screen
hdc := CreateCompatibleDC()
; Select the bitmap into the device context
obm := SelectObject(hdc, hbm)
; Get a pointer to the graphics of the bitmap, for use with drawing functions
G := Gdip_GraphicsFromHDC(hdc)
; Set the smoothing mode to antialias = 4 to make shapes appear smother (only used for vector drawing and filling)
Gdip_SetSmoothingMode(G, 4)
; Set the PixelOffsetMode to Half pixel offset = 2 to draw a perfect Rounded Rectangle
; 此参数是画出完美圆角矩形的关键
Gdip_SetPixelOffsetMode(G, 2)
Return
本章习题:让文字框在鼠标右键按下时退出。
全部代码与库文件下载地址:
https://ahk.lanzoux.com/b01nypnuh
密码:
有个小问题:我这里点击第一次的时候无法拖动
应该没问题 我都反复测试过
我这里也是
看到标题中实现拖动进来学习的,虽然还没涉足GDI,但文中的OnMessage的监听事件实现对我非常有帮助,把困扰我的问题实现窗体拖动解决了,非常感谢
讲得很详细,感谢。
这个积分是怎么来的