Page 1 of 3

How to add an extra button to the WindowTitle?

Posted: Tue Dec 26, 2006 5:20 am
by va!n
An window has normaly three buttons.... Minimize, Maximize, CloseButton...
How is it possible to add an own (4th) button to the window? If a user press on the button, i would like to minimize the app only to the systray. thanks.

I am looking for something like eMule support...

Posted: Tue Dec 26, 2006 6:39 am
by JCV
Check the sourcecode of JaPBe. Locate "Stay on Top" on files.

Posted: Tue Dec 26, 2006 1:35 pm
by netmaestro
GPI's code is good, I believe Tailbite has something similar. Also, I have a much simpler implementation here:

http://www.purebasic.fr/english/viewtopic.php?t=22476

Posted: Wed Dec 27, 2006 5:58 pm
by netmaestro
I have an improved version of this with a themed look, here's a demo:

http://www.networkmaestro.com/titlebar.exe

Here's the source and resources in a zip:

http://www.networkmaestro.com/titlebutton.zip

You will have to edit the resource file to reflect the path to the icons on your machine. Relative paths don't seem to work in resource files.

One thing to note, neither my version nor the GPI / El_Choni versions found in Japbe and Tailbite work on Vista. Not sure why, I'll have to see if it can be tweaked to support Vista too.

Posted: Wed Dec 27, 2006 6:40 pm
by Sparkie
Works great netmaestro :!:

...well except for the nasty memory leak. Adding some ReleaseDC_()'s in there seems to fix it :wink:

Posted: Wed Dec 27, 2006 6:43 pm
by netmaestro
:oops: :oops: :oops:

Dreadful coding!!!

Posted: Wed Dec 27, 2006 6:44 pm
by Sparkie
Been there...done that :wink:

Posted: Wed Dec 27, 2006 6:51 pm
by netmaestro
Will the window dc stay the same for the life of the window? If so, I could just get it, make it global, use as needed and release it once at the end. What do you think?

[edit] For my excuse, see my sig :wink:

Posted: Wed Dec 27, 2006 7:01 pm
by Fluid Byte
I don't want to picky netmaestro 'cuz this is nice work so far but I got 2 things I'd like to point out:

1.) You have take into account that users have set different heights for their titlebar like me:

Image

2.) Any chance to remove the "nasty" focus rect when you hover over the button with the mouse?

Posted: Wed Dec 27, 2006 7:03 pm
by xgp
EDITED: too late, Fluid Byte already said the same i wanted to say.


Wow!
Excellent netmaestro! :D

But, just one thing which i'm sure you already know..

It doesn't work that good with XP styles disabled.
Oh... well, here is mine:

Image


It's easy to understand why this happens, but it might (i have no idea right now) be a bit difficult to workaround it.
To make it look good on my machine, i just modified the yTop parameter in all DrawIconEx_ calls from 7 to 3.

Image


Anyway, Excellent piece of code!
xgp

Posted: Wed Dec 27, 2006 7:08 pm
by Trond

Code: Select all

; Original by William Yu
EnableExplicit

Global OldWndProc

#ANTIALIASED_QUALITY = 4
#NONANTIALIASED_QUALITY = 3

Procedure Callback(WindowID, Message, wParam, lParam)
  Protected Result = 0
  Static Pushed.l
  Protected Rect.RECT
  Protected X.l, Y.l
  Protected hDC.l
  Protected Border.l
  Protected Pen.l
  Protected PenSize.l
  Protected PB_WindowProc.l
  Protected Font
  Protected Caption.s{2}
  Select Message
    Case #WM_NCLBUTTONDOWN
      GetWindowRect_(WindowID, @Rect)
      X = lParam & $FFFF
      Y = lParam >> 16
      With Rect
        \Left   = \Right - GetSystemMetrics_(#SM_CXFRAME) - GetSystemMetrics_(#SM_CXSIZE) * 4 + 1
        \Top    = \Top + GetSystemMetrics_(#SM_CYFRAME) + 2
        \Right  = \Left + GetSystemMetrics_(#SM_CXSIZE) -1
        \Bottom = \Top + GetSystemMetrics_(#SM_CYSIZE) - 4
        If x >= \Left And x <= \Right And y >= \Top And y <= \Bottom
          Pushed = 1
          SendMessage_(WindowID, #WM_NCPAINT, 0, 0)
          Result = 0
        Else
          CallWindowProc_(OldWndProc, WindowID, Message, wParam, lParam)
          Result = 0
        EndIf
      EndWith
    Case #WM_NCLBUTTONUP
        CallWindowProc_(OldWndProc, WindowID, Message, wParam, lParam)
        Result = 0
      If Pushed
        Pushed = 0
        MessageRequester("", "Send to tray...")
      EndIf
    Case #WM_NCPAINT, #WM_NCACTIVATE
      CallWindowProc_(OldWndProc, WindowID, Message, wParam, lParam)
      hDC = GetWindowDC_(WindowID)
      GetWindowRect_(WindowID, @Rect)
      With Rect
        \Left = \Right - \Left - GetSystemMetrics_(#SM_CXFRAME) - GetSystemMetrics_(#SM_CXSIZE) * 4 + 2
        \Top = GetSystemMetrics_(#SM_CYFRAME) + 2
        \Right = \Left + GetSystemMetrics_(#SM_CXSIZE) - 2
        \Bottom = \Top + GetSystemMetrics_(#SM_CYSIZE) - 4
        If Pushed
          Border = #BDR_SUNKEN
        Else
          Border = #BDR_RAISED
        EndIf
        DrawEdge_(hDC, @Rect, Border, #BF_RECT | #BF_SOFT | #BF_MIDDLE)
        Font = CreateFont_(Int(GetSystemMetrics_(#SM_CXSIZE)*1.5), 0, 0, 0, 0, 0, 0, 0, #DEFAULT_CHARSET, 0, 0, #NONANTIALIASED_QUALITY, 0, @"Arial")
        SelectObject_(hDC, Font)
        Caption = "."
        SetBkMode_(hDC, #TRANSPARENT)
        \Right - 2
        \Bottom + 3
        DrawText_(hDC, @Caption, -1, @Rect, #DT_BOTTOM | #DT_SINGLELINE | #DT_RIGHT)
        DeleteObject_(Font)
        Result = 1
      EndWith
    Default
      Result = CallWindowProc_(OldWndProc, WindowID, Message, wParam, lParam)
  EndSelect
  ProcedureReturn Result
EndProcedure

OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget | #PB_Window_Invisible)
CreateGadgetList(WindowID(0))
TextGadget(0, 10, 10, 100, 25, "Click on the dot button to minimize window to tray")
OldWndProc = SetWindowLong_(WindowID(0), #GWL_WNDPROC, @Callback())
HideWindow(0, 0)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver

Posted: Wed Dec 27, 2006 7:31 pm
by Fluid Byte
@netmaestro: Forget my second point. I just see that you are using bitmaps.

So I guess you need to access the XP theme lib?

@Trond: Please update your system respectivley use the XP theme feature. Then you would know that this code certainly corectly places/sizes the button but lacks XP skinning representation.

Posted: Wed Dec 27, 2006 7:41 pm
by netmaestro
This code should pretty much size the button to fit correctly on the titlebar of the window. To test it, I added code to change the window style to ToolWindow and it looked right. Also, the memory leak should be fixed.

Code: Select all

;=====================================================================
; Program:            TitleBar Button Example - skinned look
; Author:             netmaestro
; Date:               December 27, 2006
; Target OS:          Microsoft Windows All
; Target Compiler:    PureBasic 4.02
; License:            Free, Unrestricted, credit appreciated
;                     but not required
; Required Resources: http://www.networkmaestro.com/TitleButton.zip
;=====================================================================

Global pencilup=LoadImage_(GetModuleHandle_(0),"IconUp",#IMAGE_ICON,0,0,0) 
Global pencilhover=LoadImage_(GetModuleHandle_(0),"IconHover",#IMAGE_ICON,0,0,0) 
Global pencilpushed=LoadImage_(GetModuleHandle_(0),"IconPushed",#IMAGE_ICON,0,0,0) 
Global WindowDC, ButtonSize, ButtonTop

Procedure ButtonAction() 
  SetGadgetText(0, GetGadgetText(0)+"Pencil... ") 
EndProcedure 

Procedure MouseOnButton() 
  GetCursorPos_(@pt.point) 
  If pt\x >= WindowX(0)+540 And pt\x <= WindowX(0)+560 
    If pt\y >=WindowY(0)+ButtonTop And pt\y <= WindowY(0)+ButtonTop+ButtonSize 
      ProcedureReturn 1 
    Else 
      ProcedureReturn 0 
    EndIf 
  EndIf 
EndProcedure 

Procedure ProcessTitleButtons() 
  If MouseOnButton() 
    While GetAsyncKeyState_(#VK_LBUTTON) & 32768 
      If MouseOnButton() 
        DrawIconEx_(WindowDC, 540,ButtonTop, pencilpushed, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
      Else 
        DrawIconEx_(WindowDC, 540,ButtonTop, pencilup, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
      EndIf 
      Delay(1) 
    Wend 
    While WindowEvent():Wend 
    If MouseOnButton() 
      ButtonAction() 
    EndIf 
    DrawIconEx_(WindowDC, 540,ButtonTop, pencilup, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
  EndIf 
EndProcedure 

Procedure CallBack(hwnd, msg, wparam, lparam) 
  result = #PB_ProcessPureBasicEvents 
  Select msg 
    Case #WM_PAINT,#WM_ACTIVATE,#WM_MOVE 
      DrawIconEx_(WindowDC, 540,ButtonTop, pencilup, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
    Case #WM_SYSCOMMAND 
      ProcessTitleButtons() 
  EndSelect 
  ProcedureReturn result 
EndProcedure 

Procedure TitleButtonHover()
  Repeat
    If GetAsyncKeyState_(#VK_LBUTTON) & 32768 = 0
      If MouseOnButton() 
        DrawIconEx_(WindowDC, 540,ButtonTop, pencilhover, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
      Else
        DrawIconEx_(WindowDC, 540,ButtonTop, pencilup, ButtonSize, ButtonSize, 0, #Null, #DI_NORMAL ) 
      EndIf
    EndIf
    Delay(50)
  ForEver
EndProcedure

OpenWindow(0,0,0,640,480,"TitleBarButton Example by netmaestro",#PB_Window_ScreenCentered|#PB_Window_SystemMenu|#PB_Window_MinimizeGadget) 
WindowDC = GetWindowDC_(WindowID(0))

wi.WINDOWINFO
wi\cbSize = SizeOf(WINDOWINFO)
GetWindowInfo_(WindowID(0), @wi)
TitleBarHeight = (wi\rcClient\top - wi\rcWindow\top)
ButtonSize = TitleBarHeight * 0.62
buttonTop  = TitleBarHeight * 0.25

SetWindowCallback(@CallBack()) 
CreateThread(@TitleButtonHover(), 0)
CreateGadgetList(WindowID(0)) 
TextGadget(0,220,200,240,200,"") 

Repeat : Until WaitWindowEvent() = #WM_CLOSE 

ReleaseDC_(WindowID(0), WindowDC)
End 

Posted: Wed Dec 27, 2006 8:43 pm
by ts-soft
it's easier to use a *.res, so no changes for path and so on:
Download the ButtonIcons.res
and add this line on top of netmeastro's code:

Code: Select all

Import "buttonicons.res" : EndImport
I hope it's usefull

Posted: Wed Dec 27, 2006 9:07 pm
by Sparkie
@Trond: I'm getting the memory leak with yours as well. Adding ReleaseDC_() fixes it.

Starts off using 2,256K with 32 GDI Objects

After moving the window back and forth past the screen edges a few times, it's using 2,688K with 1,376 GDI Objects