Change titlebar and border color of current window

Post your working scripts, libraries and tools for AHK v1.1 and older
gave92
Posts: 7
Joined: 04 Mar 2020, 08:38
Contact:

Change titlebar and border color of current window

04 Mar 2020, 09:15

The following script changes the color of the current window (titlebar + border).
You can use this on multiple windows and each will get a different background color.
By default you can use the Ctrl+Shift+W hotkey to change the current window background color.

On Windows 10 the effect will only be visible as a border color unless the app has a transparent background (e.g Settings)

ChangeWindowColor.ahk is the main script to launch.
You need both the following files in the same folder (pick the Dock.ahk file depending on your OS).

ChangeWindowColor.ahk
Dock.ahk for Windows 7
Dock.ahk for Windows 10

[Updated 07/03/2020]
Added Windows 10 version

[Updated 09/03/2020]
Handle windows hiding

Credits:
Original Dock.ahk is from:
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=30912

Tested on Windows 7 with Aero Theme and Windows 10
Last edited by gave92 on 09 Mar 2020, 03:11, edited 5 times in total.
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: Change titlebar and border color of current window

05 Mar 2020, 09:11

Very interesting, @gave92 , working fine for me on Win7 SP1 64-bit.
Thanks for sharing it.
Regards,
burque505
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

06 Mar 2020, 08:39

Nice idea! Might have a tiny bug. Have you tried it on AlwaysOnTop windows?
Attachments
ChangeWindowColor.png
ChangeWindowColor.png (40.28 KiB) Viewed 10179 times
gave92
Posts: 7
Joined: 04 Mar 2020, 08:38
Contact:

Re: Change titlebar and border color of current window

08 Mar 2020, 05:05

@rommmcek When I apply it on windows made AlwaysOnTop with

Code: Select all

WinSet, AlwaysOnTop, On, % "ahk_id " . hwnd
it works correctly but makes the window not AlwaysOnTop :)
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

08 Mar 2020, 05:45

Yea, I noticed that too. It's not a big deal. Try to run this script, then "shadow" it, then hide it...

Code: Select all

Gui, +AlwaysOnTop
Gui, add, Button, w70 h30 gGuiHide, Gui Hide
gui, show, w400 h300

GuiClose:
Esc:: ExitApp

GuiHide:
    Gui, Hide
Return
gave92
Posts: 7
Joined: 04 Mar 2020, 08:38
Contact:

Re: Change titlebar and border color of current window

08 Mar 2020, 06:32

Thanks for reporting this! That does not appear to be an issue with AlwaysOnTop windows, but with the Gui, Hide command.
The following script works in the scenario you mentioned.

Code: Select all

Gui, TEST:New, +AlwaysOnTop
Gui, TEST:Add, Button, w70 h30 gGuiHide, Gui Hide
gui, TEST:Show, w400 h300
Return

TESTGuiClose:
    Gui, TEST:Destroy
    ExitApp
Return

GuiHide:
    Gui, TEST:Hide
	; Hide the background
	WinHide, ahk_class AutoHotkeyGUI
Return

; Ctrl+Shift+S shows the hidden gui
^+s::
	WinShow, ahk_class AutoHotkeyGUI
	gui, TEST:Show, w400 h300
Return
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

08 Mar 2020, 09:25

That's not a very good solution! User might have plenty of such scripts. Do you expect to edit them all? Besides it would hide any top most Ahk gui. That's very inconvenient especially for beginners...
You could probably solve this with a window hook.
gave92
Posts: 7
Joined: 04 Mar 2020, 08:38
Contact:

Re: Change titlebar and border color of current window

09 Mar 2020, 03:14

Hello, please try the udated version in the top post, it should handle window hiding/showing automatically.
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

09 Mar 2020, 16:28

I took a look in the code. That tiny bug seems to lay in the Dock Class not in your code. So before I give it a try, could you raise the same question to the author of the Dock Class? If you will, make here a link please!
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

10 Mar 2020, 05:35

Great work! (I'm absolutely sure you know classes better then I do.)
Maybe you should look in this thread too.
bluesky
Posts: 11
Joined: 24 Dec 2020, 20:15

Re: Change titlebar and border color of current window

03 Jan 2021, 20:50

Is there a better way to set client windows always top, hide taskbar icon?
I post a reply at too: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=30912&p=374160#p374160
At first I thought Title is embedded mode, but not ..

when the border is wide enough, click the border will cause client disappear

change the code like this:

Code: Select all

If InStr(self.pos, "Title")
			{
				; SysGet, BorderWidth, 5
				
				If (via = "host")
				{
					Return this.MoveWindow(self.hwnd.Client 	;hwnd
								, Host.x+36			;x
								, Host.y-32			      		;y
								, Host.w-10	  		;width
								, Host.h-2) 	 	;height
				}

				If (via = "client")
				{
				}
			}
User avatar
kunkel321
Posts: 1194
Joined: 30 Nov 2015, 21:19

Re: Change titlebar and border color of current window

07 Jan 2021, 16:26

This is pretty cool!
Hey, how is it choosing what color to use? Is it picking them randomly?
Also, I wonder, is it possible to outline only the Titlebar, rather than the entire window?
ste(phen|ve) kunkel
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

07 Jan 2021, 18:46

bluesky wrote:
03 Jan 2021, 20:50
... click the border will cause client disappear
If we consider the border not part of the active window then click through will do the job:Gui, %GuiName%:+ToolWindow +E0x20 ; click through
kunkel321 wrote:
07 Jan 2021, 16:26
... how is it choosing what color to use?
Colors are stored here: Colors := ["0xD95319", "0xEDB120", "0x7E2F8E", "0x77AC30"]. Always is chosen first color, but they cycle:

Code: Select all

	Gui, %GuiName%:Color, % Colors[1]
	Colors.Push(Colors[1])
	Colors.RemoveAt(1)
So the 5th window you outline will have the same color as the first. You can change colors as you wish and add them as much as you want (If you add one, every 6th will be the same etc.).
kunkel321 wrote:
07 Jan 2021, 16:26
... is it possible to outline only the Titlebar
To have outlined only Titlebar use something like this:

Code: Select all

					Return this.MoveWindow(self.hwnd.Client 	;hwnd
								, Host.x-2 ;+6			;x
								, Host.y-3 ;-2     		;y
								, Host.w+5 ;-10	  		;width
								, 41) ;Host.h-2) 	 	;height
This is for Win10 120 ScreenDpi (125%), so you will have to adjust values if necessary.

Have fun!
User avatar
kunkel321
Posts: 1194
Joined: 30 Nov 2015, 21:19

Re: Change titlebar and border color of current window

08 Jan 2021, 14:25

Thanks Rommmcek! I will definitely tinker with your snippets!
Hey where does this command go Rommmcek?

I thought maybe right after the function call
[code]^+w::
WinGet, id, ID, A
SetBackgroudColor(id)
Return this.MoveWindow(self.hwnd.Client ;hwnd
, Host.x-2 ;+6 ;x
, Host.y-3 ;-2 ;y
, Host.w+5 ;-10 ;width
, 41) ;Host.h-2) ;height
return
[/code]
but that's not right... It doesn't go inside the function definition does it?

Sorry for being such a newb.
EDIT: I think I found it in the Dock.ahk code... Experimenting now...
EDIT AGAIN: Very nice -- it works like a charm!
Thanks again! :D
Last edited by kunkel321 on 08 Jan 2021, 14:53, edited 1 time in total.
ste(phen|ve) kunkel
User avatar
rommmcek
Posts: 1480
Joined: 15 Aug 2014, 15:18

Re: Change titlebar and border color of current window

08 Jan 2021, 14:46

The whole code:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
;ExitApp
;https://www.autohotkey.com/boards/viewtopic.php?f=6&p=374876#p374876

#SingleInstance Force

Enabled := ComObjError(false)

;#include %A_ScriptDir%\Dock.ahk

GuiArray := Object()
Colors := ["0xD95319", "0xEDB120", "0x7E2F8E", "0x77AC30"]

return


; Ctrl+Shift+W
; Change background color of current window
^+w::
WinGet, id, ID, A
SetBackgroudColor(id)
return


SetBackgroudColor(CurrHwnd)
{
	global Colors, GuiArray

	WinGetTitle, CurrTitle, ahk_id %CurrHwnd%
	GuiName := "GUI" CurrHwnd+0

	if (GuiArray.HasKey(GuiName))
	{
		GuiArray[GuiName] := ""
		GuiArray.Delete(GuiName)
		Gui, %GuiName%:Destroy
		Return
	}

	Gui, %GuiName%:New, +hwndGuihwnd	
	Gui, %GuiName%:+ToolWindow +E0x20 ; click through
	Gui, %GuiName%:Color, % Colors[1]
	Colors.Push(Colors[1])
	Colors.RemoveAt(1)

	WinSet, Transparent, 0, % "ahk_id " Guihwnd
	Gui, %GuiName%:Show, xCenter yCenter, %CurrTitle%, class DockGui
	WinSet, Style, -0xC00000, % "ahk_id " Guihwnd

	exDock := new Dock(CurrHwnd,Guihwnd)
	exDock.Position("Title")
	exDock.CloseCallback := Func("CloseCallback").Bind(GuiName)
	GuiArray[GuiName] := exDock
	
	WinActivate, % "ahk_id " . CurrHwnd
	WinSet, Transparent, 255, % "ahk_id " . Guihwnd
	
	Return
}

CloseCallback(GuiName)
{
	Gui, %GuiName%:Destroy
}

/*
	Class Dock
		Attach a window to another

	Author
		Marco Gavelli

	Original Author
		Soft (visionary1 ???)
	
	version
		0.1 (2017.04.20)
		0.2 (2017.05.06)
		0.2.1 (2017.05.07)
		0.2.1.1 bug fixed (2017.05.09)
		0.2.2 testing multiple docks... (2017.05.09)

	License
		WTFPL (http wtfpl.net /)  Broken Link for safety

	Dev env
		Windows 10 pro x64
		AutoHotKey H v1.1.25.01 32bit

	To Do...
		Multiple Dock, group windows...

	thanks to
		Helgef for overall coding advices
*/
class Dock
{
	static EVENT_OBJECT_LOCATIONCHANGE := 0x800B
	, EVENT_OBJECT_FOCUS := 0x8005, EVENT_OBJECT_DESTROY := 0x8001
	, EVENT_MIN := 0x00000001, EVENT_MAX := 0x7FFFFFFF ;for debug
	, EVENT_SYSTEM_MINIMIZESTART := 0x0016
	, EVENT_SYSTEM_MINIMIZEEND := 0x0017
	, EVENT_SYSTEM_FOREGROUND := 0x0003
	, EVENT_OBJECT_HIDE := 0x8003
	, EVENT_OBJECT_SHOW := 0x8002

	/*
		Instance := new Dock(Host hwnd, Client hwnd, [Callback], [CloseCallback])

			Host hwnd
				hwnd of a Host window

			Client hwnd
				hwnd of a window that follows Host window (window that'll be attached to a Host window)

			[Callback]
				a func object, or a bound func object
				if omitted, default EventsHandler will be used, which is hard-coded in 'Dock.EventsHandler'
				To construct your own events handler, I advise you to see Dock.EventsHandler first

			[CloseCallback]
				a func object, or a bound func object
				called when Host window is destroyed, see 'Dock Example.ahk' for practical usuage
	*/
	__New(Host, Client, Callback := "", CloseCallback := "")
	{
		this.hwnd := []
		this.hwnd.Host := Host
		this.hwnd.Client := Client
		; WinSet, ExStyle, +0x80, % "ahk_id " this.hwnd.Client

		this.Bound := []

		this.Callback := IsObject(Callback) ? Callback : ObjBindMethod(Dock.EventsHandler, "Calls")
		this.CloseCallback := IsFunc(CloseCallback) || IsObject(CloseCallback) ? CloseCallback

		/*
			lpfnWinEventProc
		*/
		this.hookProcAdr := RegisterCallback("_DockHookProcAdr",,, &this)

		/*
			idProcess
		*/
		WinGet, idProcess, PID, % "ahk_id " . this.hwnd.Host
		;idProcess := 0

		/*
			idThread
		*/
		idThread := DllCall("GetWindowThreadProcessId", "Ptr", this.hwnd.Host, "Int", 0)
		;idThread := 0

		; DllCall("CoInitialize", "Int", 0)

		this.Hook := DllCall("SetWinEventHook"
				, "UInt", Dock.EVENT_SYSTEM_FOREGROUND 		;eventMin
				, "UInt", Dock.EVENT_OBJECT_LOCATIONCHANGE 	;eventMax
				, "Ptr", 0				  	;hmodWinEventProc
				, "Ptr", this.hookProcAdr 			;lpfnWinEventProc
				, "UInt", idProcess			 	;idProcess
				, "UInt", idThread			  	;idThread
				, "UInt", 0)					;dwFlags
				
		this.enable := 1
	}

	/*
		Instance.Unhook()
			unhooks Dock and frees memory
	*/
	Unhook()
	{
		DllCall("UnhookWinEvent", "Ptr", this.Hook)		
		; DllCall("CoUninitialize")
		DllCall("GlobalFree", "Ptr", this.hookProcAdr)
		this.Hook := ""
		this.hookProcAdr := ""
		this.Callback := ""
		; WinSet, ExStyle, -0x80, % "ahk_id " this.hwnd.Client		
	}

	__Delete()
	{
		this.Delete("Bound")

		If (this.Hook)
			this.Unhook()

		this.CloseCallback := ""
	}

	/*
		provisional
	*/
	Add(hwnd, pos := "")
	{
		static last_hwnd := 0

		this.Bound.Push( new this( !NumGet(&this.Bound, 4*A_PtrSize) ? this.hwnd.Client : last_hwnd, hwnd ) )

		If pos Contains Top,Bottom,R,Right,L,Left
			this.Bound[NumGet(&this.Bound, 4*A_PtrSize)].Position(pos)

		last_hwnd := hwnd
	}

	/*
		Instance.Position(pos)
			pos - sets position to dock client window
				Top - sets to Top side of the host window
				Bottom - sets to bottom side of the host window
				R or Right - right side
				L or Left -  left side
	*/
	Position(pos)
	{
		this.pos := pos
		Return this.EventsHandler.EVENT_OBJECT_LOCATIONCHANGE(this, "host")
	}

	/*
		Default EventsHandler
	*/
	class EventsHandler extends Dock.HelperFunc
	{
		Calls(self, hWinEventHook, event, hwnd)
		{
			Critical

			If (hwnd = self.hwnd.Host)
			{
				Return this.Host(self, event)
			}

			If (hwnd = self.hwnd.Client)
			{
				Return this.Client(self, event)
			}
		}

		Host(self, event)
		{
			If (event = Dock.EVENT_OBJECT_HIDE)
			{
				WinHide, % "ahk_id " self.hwnd.Client
			}
			
			If (event = Dock.EVENT_OBJECT_SHOW)
			{
				WinShow, % "ahk_id " self.hwnd.Client
			}
			
			If (event = Dock.EVENT_SYSTEM_MINIMIZESTART)
			{
				self.enable := 0
			}
			
			If (event = Dock.EVENT_SYSTEM_MINIMIZEEND)
			{				
				Sleep, 500 ; Wait animation
				WinSet, Transparent, 0, % "ahk_id " self.hwnd.Client
				this.EVENT_OBJECT_LOCATIONCHANGE(self, "host")				
				this.EVENT_SYSTEM_FOREGROUND(self.hwnd.Client)
				this.EVENT_SYSTEM_FOREGROUND(self.hwnd.Host)
				WinSet, Transparent, 255, % "ahk_id " self.hwnd.Client
				self.enable := 1
			}
			
			If (event = Dock.EVENT_SYSTEM_FOREGROUND AND self.enable)
			{				
				WinSet, Transparent, 0, % "ahk_id " self.hwnd.Client
				this.EVENT_SYSTEM_FOREGROUND(self.hwnd.Client)
				this.EVENT_SYSTEM_FOREGROUND(self.hwnd.Host)
				this.EVENT_SYSTEM_FOREGROUND(self.hwnd.Host) ; why??
				WinSet, Transparent, 255, % "ahk_id " self.hwnd.Client				
				Return ""
			}

			If (event = Dock.EVENT_OBJECT_LOCATIONCHANGE AND self.enable)
			{
				Return this.EVENT_OBJECT_LOCATIONCHANGE(self, "host")
			}

			If (event = Dock.EVENT_OBJECT_DESTROY)
			{
				self.Unhook()

				If (IsFunc(self.CloseCallback) || IsObject(self.CloseCallback))
					Return self.CloseCallback()
			}
		}

		Client(self, event)
		{
			If (event = Dock.EVENT_SYSTEM_FOREGROUND)
			{
			}

			If (event = Dock.EVENT_OBJECT_LOCATIONCHANGE)
			{
			}
		}

		/*
			Called when host window got focus
			without this, client window can't be showed (can't set to top)
		*/
		EVENT_SYSTEM_FOREGROUND(hwnd)
		{
			Return this.WinSetTop(hwnd)
		}

		/*
			Called when host window is moved
		*/
		EVENT_OBJECT_LOCATIONCHANGE(self, via)
		{
			Host := this.WinGetPos(self.hwnd.Host)
			Client := this.WinGetPos(self.hwnd.Client)

			If InStr(self.pos, "Title")
			{
				; SysGet, BorderWidth, 5
				
				If (via = "host")
				{
					Return this.MoveWindow(self.hwnd.Client 	;hwnd
								, Host.x-2 ;+6			;x
								, Host.y-3 ;-2     		;y
								, Host.w+5 ;-10	  		;width
								, 41) ;Host.h-2) 	 	;height
				}

				If (via = "client")
				{
				}
			}
			
			If InStr(self.pos, "Top")
			{
				If (via = "host")
				{
					Return this.MoveWindow(self.hwnd.Client 	;hwnd
								, Host.x		;x
								, Host.y - Client.h 	;y
								, Client.w	  	;width
								, Client.h) 		;height
				}

				If (via = "client")
				{
					Return this.MoveWindow(self.hwnd.Host	   	;hwnd
								, Client.x	  	;x
								, Client.y + Client.h   ;y
								, Host.w		;width
								, Host.h)	   	;height
				}
			}

			If InStr(self.pos, "Bottom")
			{
				If (via = "host")
				{		   
					Return this.MoveWindow(self.hwnd.Client	 	;hwnd
								, Host.x		;x
								, Host.y + Host.h   	;y
								, Client.w	  	;width
								, Client.h)	 	;height
				}

				If (via = "client")
				{
					Return this.MoveWindow(self.hwnd.Host	   	;hwnd
								, Client.x	  	;x
								, Client.y - Host.h 	;y
								, Host.w		;width
								, Host.h)	   	;height
				}
			}

			If InStr(self.pos, "R")
			{
				If (via = "host")
				{
					Return this.MoveWindow(self.hwnd.Client	 	;hwnd
								, Host.x + Host.w   	;x
								, Host.y		;y
								, Client.w	  	;width
								, Client.h)	 	;height	
				}

				If (via = "client")
				{
					Return this.MoveWindow(self.hwnd.Host	   	;hwnd
								, Client.x - Host.w 	;x
								, Client.y	  	;y
								, Host.w		;width
								, Host.h)	   	;height
				}
			}

			If InStr(self.pos, "L")
			{
				If (via = "host")
				{
					Return this.MoveWindow(self.hwnd.Client	 	;hwnd
								, Host.x - Client.w 	;x
								, Host.y		;y
								, Client.w	  	;width
								, Client.h)	 	;height	
				}

				If (via = "client")
				{
					Return this.MoveWindow(self.hwnd.Host	   	;hwnd
								, Client.x + Client.w   ;x
								, Client.y	  	;y
								, Host.w		;width
								, Host.h)	   	;height	
				}
			}
		}
	}

	class HelperFunc
	{
		WinGetPos(hwnd)
		{
			WinGetPos, hX, hY, hW, hH, % "ahk_id " . hwnd
			Return {x: hX, y: hY, w: hW, h: hH}
		}

		WinSetTop(hwnd)
		{
			WinSet, AlwaysOnTop, On, % "ahk_id " . hwnd
			WinSet, AlwaysOnTop, Off, % "ahk_id " . hwnd
		}

		MoveWindow(hwnd, x, y, w, h)
		{			
			DllCall("MoveWindow", "Ptr", hwnd, "Int", x, "Int", y, "Int", w, "Int", h, "Int", 1)
			tmp := 0 "-" 0 " W" w " H" h " R0-0"
			WinSet, Region, %tmp%, % "ahk_id " hwnd
			Return ""
		}

		Run(Target)
		{
			Try Run, % Target,,, OutputVarPID
			Catch, 
				Throw, "Couldn't run " Target

			WinWait, % "ahk_pid " OutputVarPID

			Return WinExist("ahk_pid " OutputVarPID)
		}
	}
}

_DockHookProcAdr(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
	this := Object(A_EventInfo)
	this.Callback.Call(this, hWinEventHook, event, hwnd)
}
User avatar
kunkel321
Posts: 1194
Joined: 30 Nov 2015, 21:19

Re: Change titlebar and border color of current window

08 Jan 2021, 15:03

rommmcek wrote:
08 Jan 2021, 14:46
The whole code ....
Cool thanks.

I actually have a quicky one liner in my main stript to pin the current window to the top.

Code: Select all

;#################### PIN WINDOW TO TOP #########################
^SPACE::  Winset, Alwaysontop, , A
return
It always bugged me that there's no visual indicator of the window being pinned. I'll add this so it appears when the window is pinned, then kill the process when it's not pinned.
ste(phen|ve) kunkel
User avatar
kunkel321
Posts: 1194
Joined: 30 Nov 2015, 21:19

Re: Change titlebar and border color of current window

09 Jan 2021, 12:02

Re: Flag for pinning window to the top.... It occurs to me that using SetWinTitle might be a more sensible way to do this, but I"ll post a new thread for that, because it's off-topic.
ste(phen|ve) kunkel
Skrell
Posts: 307
Joined: 23 Jan 2014, 12:05

Re: Change titlebar and border color of current window

03 Feb 2021, 17:05

It would be amazing if this script could be grown into automatically coloring windows based on their icon! There was an old freeware app that could do this called aeroblend but the biggest flaw it had (IMO) was that you couldn't force the colors of designated windows to be a saved value. In other words, it ALWAYS colored the windows based on the icon, so explorer.exe for instance would end up being like 4 different colors as you browed through the different folder icons. It would have been GREAT if I could just have made explorer.exe always the same color but color, say firefox, dynamically based on its icon.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 204 guests