Statusbar.pbi
Code: Select all
; iBackColor
#TEXT_EX_TRANSPARENT = -1
; iTextPos
#TEXT_EX_LEFT = #DT_LEFT | #DT_VCENTER | #DT_SINGLELINE
#TEXT_EX_CENTER = #DT_CENTER | #DT_VCENTER | #DT_SINGLELINE
#TEXT_EX_RIGHT = #DT_RIGHT | #DT_VCENTER | #DT_SINGLELINE
; flgBlinkStatus
#TEXT_EX_BLINK_OFF = 0
#TEXT_EX_BLINK_NORMAL = 1
#TEXT_EX_BLINK_REVERSE = 2
; internally used
#_TEXT_EX_MAX_FIELDS = 16
#_TEXT_EX_BLINK_SPEED_DEFAULT = 500
#_TEXT_EX_BLINKING_OFF = -1
#_TEXT_EX_BLINKING_STATE_A = 0
#_TEXT_EX_BLINKING_STATE_B = 1
Macro _LOWORD (word)
(word & $FFFF)
EndMacro
Macro _HIWORD (word)
((word >> 16) & $FFFF)
EndMacro
Procedure _SB_FreeString (*StringAddress)
Protected String.String
PokeI (@String, *StringAddress)
EndProcedure
Structure T_SB_TEXT_ITEM
iField.i
sText.s
iTextPos.i
iForeColor.i
iBackColor.i
hFont.i
flgBlinking.i
flgBlinkStyle.i
EndStructure
Structure T_SB_TEXT
hStatus.i
hTimer.i
iBlinkSpeed.i
*aiFields.T_SB_TEXT_ITEM[#_TEXT_EX_MAX_FIELDS]
EndStructure : Define tSB_TEXT.T_SB_TEXT
Procedure _SB_WinMain_Subclass (hWnd, Msg, wParam, lParam)
Shared tSB_TEXT.T_SB_TEXT
Protected *tSB_TEXT_ITEM.T_SB_TEXT_ITEM
Protected *DrawItem.DRAWITEMSTRUCT, tRECT.RECT
Protected *oldWinProc = GetProp_(hWnd, "oldWinProc")
Protected hStatus, hBrush
Protected iTextPos, iForeColor, iBackColor, k
Protected flgTransparent, flgSkipDraw
Select Msg
Case #WM_SIZING
; update status bar
hStatus = tSB_TEXT\hStatus
InvalidateRect_(hStatus, 0, 0)
UpdateWindow_(hStatus)
Case #WM_NCDESTROY
; cleanup props
RemoveProp_(hWnd, "oldWinProc")
; destroy timer (if any)
If tSB_TEXT\hTimer > 0
KillTimer_(0, tSB_TEXT\hTimer)
EndIf
; cleanup allocated structures
For k = 0 To #_TEXT_EX_MAX_FIELDS - 1
If tSB_TEXT\aiFields[k] > 0
_SB_FreeString(@tSB_TEXT\aiFields[k]\sText)
FreeMemory(tSB_TEXT\aiFields[k])
EndIf
Next
Case #WM_DRAWITEM
hStatus = tSB_TEXT\hStatus
If wParam = GetDlgCtrlID_(hStatus)
flgSkipDraw = #False
*DrawItem = lParam
*tSB_TEXT_ITEM = tSB_TEXT\aiFields[*DrawItem\itemData]
iTextPos = *tSB_TEXT_ITEM\iTextPos
iForeColor = *tSB_TEXT_ITEM\iForeColor
iBackColor = *tSB_TEXT_ITEM\iBackColor
If iBackColor = #TEXT_EX_TRANSPARENT
flgTransparent = #True
Else
flgTransparent = #False
EndIf
; some margins
If *tSB_TEXT_ITEM\iField = 0
*DrawItem\rcItem\left + 1
EndIf
*DrawItem\rcItem\right - 3
If *tSB_TEXT_ITEM\flgBlinking <> #_TEXT_EX_BLINKING_OFF
; text blinking
Select *tSB_TEXT_ITEM\flgBlinkStyle
Case #TEXT_EX_BLINK_REVERSE
If *tSB_TEXT_ITEM\flgBlinking = #_TEXT_EX_BLINKING_STATE_B
Swap iForeColor, iBackColor
EndIf
Case #TEXT_EX_BLINK_NORMAL
If *tSB_TEXT_ITEM\flgBlinking = #_TEXT_EX_BLINKING_STATE_B
flgSkipDraw = #True
EndIf
EndSelect
EndIf
SetTextColor_(*DrawItem\hDC, iForeColor)
If flgTransparent
SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
Else
SetBkMode_(*DrawItem\hDC, #OPAQUE)
SetBkColor_(*DrawItem\hDC, iBackColor)
hBrush = CreateSolidBrush_(iBackColor)
FillRect_(*DrawItem\hDC, *DrawItem\rcItem, hBrush)
DeleteObject_(hBrush)
EndIf
If flgSkipDraw = #False
InflateRect_(*DrawItem\rcItem, -2, 0)
If *tSB_TEXT_ITEM\hFont <> #Null
SelectObject_(*DrawItem\hDC, *tSB_TEXT_ITEM\hFont)
EndIf
DrawText_(*DrawItem\hDC, *tSB_TEXT_ITEM\sText, -1, *DrawItem\rcItem, iTextPos)
EndIf
ProcedureReturn 1
EndIf
EndSelect
; This will call the original winproc of the parent window or it will chain up
; to the new winproc you have defined with SetWindowCallback() in PB
; In short: it's compatible with your own SetWindowCallback()
ProcedureReturn CallWindowProc_(*oldWinProc, hWnd, Msg, wParam, lParam)
EndProcedure
Procedure _SB_StatusBar_Subclass (hWnd, Msg, wParam, lParam)
Protected tRECT.RECT
Protected k, X, Y
Protected *fpFunc
Protected *oldWinProc = GetProp_(hWnd, "oldWinProc")
Select Msg
Case #WM_NCDESTROY
; cleanup
RemoveProp_(hWnd, "oldWinProc")
RemoveProp_(hWnd, "callback")
Case #WM_LBUTTONDBLCLK, #WM_RBUTTONDBLCLK, #WM_LBUTTONDOWN, #WM_RBUTTONDOWN
X = _LOWORD (lParam) ; X mouse coordinates
Y = _HIWORD (lParam) ; Y mouse coordinates
*fpFunc = GetProp_(hWnd, "callback") ; callback address
If *fpFunc > 0
While SendMessage_(hWnd, #SB_GETRECT, k, @tRECT)
If X < tRECT\right
; call callback procedure
CallFunctionFast(*fpFunc, Msg, k, X, Y)
Break
EndIf
k + 1
Wend
EndIf
EndSelect
ProcedureReturn CallWindowProc_(*oldWinProc, hWnd, Msg, wParam, lParam)
EndProcedure
Procedure _SB_TimerEvent()
Shared tSB_TEXT.T_SB_TEXT
Protected k, nBlinkingCount
For k = 0 To #_TEXT_EX_MAX_FIELDS - 1
If tSB_TEXT\aiFields[k] > 0
If tSB_TEXT\aiFields[k]\flgBlinking <> #_TEXT_EX_BLINKING_OFF
; something found
nBlinkingCount + 1
; invert status
If tSB_TEXT\aiFields[k]\flgBlinking = #_TEXT_EX_BLINKING_STATE_A
tSB_TEXT\aiFields[k]\flgBlinking = #_TEXT_EX_BLINKING_STATE_B
Else
tSB_TEXT\aiFields[k]\flgBlinking = #_TEXT_EX_BLINKING_STATE_A
EndIf
EndIf
EndIf
Next
If nBlinkingCount > 0
; maybe we could invalidate only a smaller region here ...
; but probabably it's not worth the effort
InvalidateRect_(tSB_TEXT\hStatus, 0, 1)
Else
; if there is no need for timer anymore ...
KillTimer_(0, tSB_TEXT\hTimer)
tSB_TEXT\hTimer = 0 ; fix
EndIf
EndProcedure
Procedure StatusBar_GrabClickEvents (hWnd, *fpFunc)
SetProp_(hWnd, "oldWinProc", GetWindowLongPtr_(hWnd, #GWL_WNDPROC))
SetProp_(hWnd, "callback", *fpFunc)
SetWindowLongPtr_(hWnd, #GWL_WNDPROC, @_SB_StatusBar_Subclass())
EndProcedure
Procedure.i StatusBar_SetBlinkSpeed (iBlinkSpeed)
Shared tSB_TEXT.T_SB_TEXT
If iBlinkSpeed < 100
iBlinkSpeed = 100
ElseIf iBlinkSpeed > 1000
iBlinkSpeed = 1000
EndIf
If iBlinkSpeed <> tSB_TEXT\iBlinkSpeed
; if speed changed
If tSB_TEXT\hTimer > 0
KillTimer_(0, tSB_TEXT\hTimer)
EndIf
tSB_TEXT\iBlinkSpeed = iBlinkSpeed
tSB_TEXT\hTimer = SetTimer_(0, 0, tSB_TEXT\iBlinkSpeed, @_SB_TimerEvent())
EndIf
If tSB_TEXT\hTimer = 0
ProcedureReturn #False
EndIf
ProcedureReturn #True
EndProcedure
Procedure.i StatusBar_SetBlink (hStatus, iField, flgBlinkStatus)
Shared tSB_TEXT.T_SB_TEXT
If iField < 0 Or iField > #_TEXT_EX_MAX_FIELDS - 1
; out of range
ProcedureReturn #False
EndIf
If tSB_TEXT\aiFields[iField] = 0
; field not initialized
ProcedureReturn #False
EndIf
If flgBlinkStatus < #TEXT_EX_BLINK_OFF Or flgBlinkStatus > #TEXT_EX_BLINK_REVERSE
ProcedureReturn #False ; invalid blink style
EndIf
If (tSB_TEXT\aiFields[iField]\iBackColor = #TEXT_EX_TRANSPARENT) And (flgBlinkStatus = #TEXT_EX_BLINK_REVERSE)
ProcedureReturn #False ; blink style not compatible with transparent background
EndIf
If flgBlinkStatus = #TEXT_EX_BLINK_OFF
tSB_TEXT\aiFields[iField]\flgBlinking = #_TEXT_EX_BLINKING_OFF
InvalidateRect_(hStatus, 0, 0) ; fix
ProcedureReturn #True
EndIf
tSB_TEXT\aiFields[iField]\flgBlinking = #_TEXT_EX_BLINKING_STATE_A
tSB_TEXT\aiFields[iField]\flgBlinkStyle = flgBlinkStatus
If tSB_TEXT\hTimer = 0 ; first time
If tSB_TEXT\iBlinkSpeed = 0
tSB_TEXT\iBlinkSpeed = #_TEXT_EX_BLINK_SPEED_DEFAULT
EndIf
tSB_TEXT\hTimer = SetTimer_(0, 0, tSB_TEXT\iBlinkSpeed, @_SB_TimerEvent())
If tSB_TEXT\hTimer = 0
tSB_TEXT\aiFields[iField]\flgBlinking = #_TEXT_EX_BLINKING_OFF
ProcedureReturn #False
EndIf
EndIf
ProcedureReturn #True
EndProcedure
Procedure.i StatusBar_SetText (hStatus, iField, sText.s, iForeColor, iBackColor = #TEXT_EX_TRANSPARENT, iTextPos = #TEXT_EX_LEFT, hFont = #Null)
Shared tSB_TEXT.T_SB_TEXT
Protected tSB_TEXT_ITEM.T_SB_TEXT_ITEM
Protected hWnd = GetParent_(hStatus)
If iField < 0 Or iField > #_TEXT_EX_MAX_FIELDS - 1 ; out of range
ProcedureReturn #False
EndIf
If GetProp_(hWnd, "oldWinProc") = 0 ; first time only
tSB_TEXT\hStatus = hStatus
SetProp_(hWnd, "oldWinProc", GetWindowLongPtr_(hWnd, #GWL_WNDPROC))
SetWindowLongPtr_(hWnd, #GWL_WNDPROC, @_SB_WinMain_Subclass())
EndIf
If tSB_TEXT\aiFields[iField] = 0 ; if it's the first time this field is referenced
tSB_TEXT\aiFields[iField] = AllocateMemory(SizeOf(T_SB_TEXT_ITEM))
If tSB_TEXT\aiFields[iField] = 0 ; out of memory
ProcedureReturn #False
EndIf
; first time init
With tSB_TEXT\aiFields[iField]
\iField = iField
\flgBlinking = #_TEXT_EX_BLINKING_OFF
EndWith
EndIf
; these can be changed calling StatusBar_SetText() again
With tSB_TEXT\aiFields[iField]
\sText = sText
\iTextPos = iTextPos
\iForeColor = iForeColor
\iBackColor = iBackColor
\hFont = hFont
EndWith
SendMessage_(hStatus, #SB_SETTEXT, iField | #SBT_OWNERDRAW, iField)
ProcedureReturn #True
EndProcedure
Test program
Code: Select all
EnableExplicit
; *******************
; ****** TEST *******
; *******************
XIncludeFile "Statusbar.pbi"
Enumeration
#WIN_MAIN
#SBG_STATUS
#EDG_EDITOR
EndEnumeration
Procedure.s ControlGetFontName (hWnd)
Protected tLOGFONT.LOGFONT
Protected hWrkFont, sFontName.s
hWrkFont = SendMessage_(hWnd, #WM_GETFONT, 0, 0)
If hWrkFont
GetObject_(hWrkFont, SizeOf(LOGFONT), @tLOGFONT)
sFontName = PeekS(@tLOGFONT\lfFacename[0])
EndIf
ProcedureReturn sFontName
EndProcedure
Procedure.i ControlGetFontSize (hWnd)
Protected tLOGFONT.LOGFONT
Protected hWrkFont, iFontSize
hWrkFont = SendMessage_(hWnd, #WM_GETFONT, 0, 0)
If hWrkFont
GetObject_(hWrkFont, SizeOf(LOGFONT), @tLOGFONT)
iFontSize = tLOGFONT\lfHeight: If iFontSize < 0: iFontSize = -iFontSize: EndIf
iFontSize = iFontSize * 72.0 / GetDeviceCaps_(GetDC_(GetDesktopWindow_()), #LOGPIXELSY)
EndIf
ProcedureReturn iFontSize
EndProcedure
Procedure CallBack_StatusBarClickEvents (iMessage, iField, iXPos, iYPos)
SetActiveGadget(#EDG_EDITOR)
Select iMessage
Case #WM_RBUTTONDOWN
AddGadgetItem(#EDG_EDITOR, -1, "RMB clicked on " + Str(iField) + " at (" + Str(iXPos) + ", " + Str(iYPos) + ")")
Case #WM_LBUTTONDOWN
AddGadgetItem(#EDG_EDITOR, -1, "LMB clicked on " + Str(iField) + " at (" + Str(iXPos) + ", " + Str(iYPos) + ")")
Case #WM_LBUTTONDBLCLK
AddGadgetItem(#EDG_EDITOR, -1, "LMB double clicked on " + Str(iField) + " at (" + Str(iXPos) + ", " + Str(iYPos) + ")")
Case #WM_RBUTTONDBLCLK
AddGadgetItem(#EDG_EDITOR, -1, "RMB double clicked on " + Str(iField) + " at (" + Str(iXPos) + ", " + Str(iYPos) + ")")
EndSelect
EndProcedure
Procedure CallBack_MainWindow (hWnd, Msg, wParam, lParam)
Select Msg
Case #WM_ERASEBKGND
InvalidateRect_(GadgetID(#EDG_EDITOR), 0, 0)
UpdateWindow_(GadgetID(#EDG_EDITOR))
ProcedureReturn 1
Case #WM_SIZE
ResizeGadget(#EDG_EDITOR, #PB_Ignore, #PB_Ignore, WindowWidth(#WIN_MAIN), WindowHeight(#WIN_MAIN)- StatusBarHeight(#SBG_STATUS))
EndSelect
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure Main()
Protected iEvent, hStatus, nFontBold
If OpenWindow(#WIN_MAIN, 10, 10, 640, 480, "Main Window", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
hStatus = CreateStatusBar(#SBG_STATUS, WindowID(#WIN_MAIN))
AddStatusBarField(110)
AddStatusBarField(110)
AddStatusBarField(110)
AddStatusBarField(110)
AddStatusBarField(#PB_Ignore) ; autosize
EditorGadget(#EDG_EDITOR, 0, 0, 640, 480 - StatusBarHeight(#SBG_STATUS))
nFontBold = LoadFont(#PB_Any, ControlGetFontName(StatusBarID(#SBG_STATUS)), ControlGetFontSize(StatusBarID(#SBG_STATUS)), #PB_Font_Bold)
; this call enable you to trap click events in the statusbar
StatusBar_GrabClickEvents (hStatus, @CallBack_StatusBarClickEvents())
; black on white, right aligned
StatusBar_SetText (hStatus, 0, "FIELD ZERO", #Black, #White, #TEXT_EX_RIGHT)
; yellow on red, centered
StatusBar_SetText (hStatus, 1, "FIELD ONE", #Yellow, #Red, #TEXT_EX_CENTER)
; field 1 blinking in reverse (swapping colors)
StatusBar_SetBlink (hStatus, 1, #TEXT_EX_BLINK_REVERSE)
; use #TEXT_EX_BLINK_OFF to stop the blink
;StatusBar_SetBlink (hStatus, 1, #TEXT_EX_BLINK_OFF)
; blue on cyan, centered
StatusBar_SetText (hStatus, 2, "FIELD TWO", #Blue, #Cyan, #TEXT_EX_CENTER)
; field 2 blinking normally (text visible / invisible)
StatusBar_SetBlink (hStatus, 2, #TEXT_EX_BLINK_NORMAL)
; black on transparent (statusbar color), centered
StatusBar_SetText (hStatus, 3, "FIELD THREE", #Black, #TEXT_EX_TRANSPARENT, #TEXT_EX_CENTER)
; field 3 blinking normally (text visible / invisible)
StatusBar_SetBlink (hStatus, 3, #TEXT_EX_BLINK_NORMAL)
; black on transparent (statusbar color), left aligned, font bold
StatusBar_SetText (hStatus, 4, "FIELD FOUR", #Black, #TEXT_EX_TRANSPARENT, #TEXT_EX_LEFT, FontID(nFontBold))
; set speed for blinking (the speed is the same for every field, default is 500 ms)
StatusBar_SetBlinkSpeed(700)
; you can still use SetWindowCallback() if you need it
SetWindowCallback (@CallBack_MainWindow())
Repeat
iEvent = WaitWindowEvent()
Until iEvent = #PB_Event_CloseWindow
FreeFont(nFontBold)
EndIf
EndProcedure
Main()