Code archives/Miscellaneous/Simple Win32 GUI

This code has been declared by its author to be Public Domain code.

Download source code

Simple Win32 GUI by Nilium
(Posted 21 years ago)
This is a work in progress and I'll be updating it as I go.

Basically, this is a GUI I'm writing that uses an OO interface. Downside? It's Win32 only.

Given that, Mac/Linux zealots/users may want to turn away right now. Complaining about how it doesn't support your OS is a waste of time because I won't be including them.

For the sake of example, here's a simple window with a button (the only two controls currently implemented):
Local wnd:IWindow = New IWindow
' Window caption
wnd.Caption = "Blah foo"
' Set the position
wnd.Position( 32, 32 )
' Set the size
wnd.Resize( 512, 384 )
' When called before the window is built
' it sets it such that the window is
' visible when built
wnd.Show( )

Local btn:IButton = New IButton
' Rinse
btn.Caption = "Close Window"
' And
btn.Position( 12, 12 )
' Repeat
btn.Resize( 128, 24 )
' Set the parent of the button
' This only works BEFORE the control is built,
' so if you call IControl.Build on the control
' then its parent is set in stone, end of story
btn.SetParent( wnd )

wnd.Build( )

' Main loop
While Not AppTerminate( )
    
    ' Wait for an event
    WaitEvent( )
    
    ' Process the event
    Select CurrentEvent.Id
        ' These are all fairly obvious event names
        Case WindowClosed
            Exit
            
        Case ButtonPressed
            If CurrentEvent.source = btn Then
                Exit
            EndIf
            
        Default
        
    End Select
    
Wend

' Free the window (works for all controls)
wnd.Dispose( )
Strict

Private

Global windows:TList = New TList
Global instance% = GetModuleHandleA(Null)
Global iwndclass$ = "IndigoWNDCLASS"
CreateWndClass( iwndclass, CS_VREDRAW|CS_HREDRAW, IWndProc )

Function GetControl:IControl( hwnd:Int )
    For Local i:IControl = EachIn windows
        If i.hwnd = hwnd Then Return i
    Next
    Return Null
End Function

Function IWndProc:Int( hwnd%, msg%, wparam%, lparam% )
    Local ctl:IControl = GetControl( hwnd )
    If ctl Then Return ctl.HandleMessage( msg, wparam, lparam )
    Return DefWindowProcW( hwnd, msg, wparam, lparam )
End Function

Function CreateWndClass:TBank( name$, style%, wndProc@ Ptr )
    Local c:TBank = TBank.Create( 40+name.Length*2+2 )
    memset_(c.Buf( ), 0, 40)
    c.PokeInt( 0, style )
    c.PokeInt( 4, Int( wndProc ) )
    c.PokeInt( 16, instance )
    c.PokeInt( 20, LoadIconA( 0, Byte Ptr(32512) ) )
    c.PokeInt( 24, LoadCursorA( 0, Byte Ptr(IDC_ARROW) ) )
    c.PokeInt( 28, 16 )
    Local mem@ Ptr = name.ToWString( )
    MemCopy( c.Buf( )+40, mem, name.Length*2+2 )
    MemFree( mem )
    c.PokeInt( 36, Int( c.Buf( )+40 ) )
    RegisterClassW( c.Buf( ) )
    Return c
End Function

Public

' Generic events
Const ControlEnumStart      =0
Const ControlMoved          =ControlEnumStart+1
Const ControlResized        =ControlMoved+1
Const ControlAction         =ControlResized+1
Const ControlHidden         =ControlAction+1
Const ControlShown          =ControlHidden+1
Const ControlFocusGained    =ControlShown+1
Const ControlFocusLost      =ControlFocusGained+1
Const ControlEnumTop        =32

Type IControl
    Method New( )
        _wndLink = windows.AddLast( Self )
    End Method
    
    Field child:TList = New TList
    Field parent:IControl
    Field hwnd%
    Field x%, y%, w%, h%
    Field link:TLink
    Field _wndLink:TLink
    
    ' Internal function that really wouldn't be called from something like the main loop
    Method HandleMessage%( msg%, wparam%, lparam% )
        Select msg
            Case WM_CREATE
                For Local i:IControl = EachIn child
                    i.Build( )
                Next
                Return 0
            Case WM_DESTROY
                Dispose( )
                Return 0
            Case WM_COMMAND
                If lparam <> 0 And lparam <> hwnd Then
                    Local ctl:IControl = GetControl( lparam )
                    If ctl Then Return ctl.HandleMessage( msg, wparam, lparam )
                EndIf
        End Select
        Return DefWindowProcW( hwnd, msg, wparam, lparam )
    End Method
    
    ' Abstract method to 'compile' the control with its initial values
    Method Build( ) Abstract
    
    ' Frees the control and all children
    Method Dispose( )
        If Not hwnd Then Return
        Hide( )
        If child Then
            For Local i:IControl = EachIn child
                i.Dispose( )
            Next
            child.Clear( )
            child = Null
        EndIf
        DestroyWindow( hwnd )
        If link Then link.Remove( ) link = Null
        If _wndLink Then _wndLink.Remove( ) _wndLink = Null
        hwnd = 0
        parent = Null
        x = 0 y = 0 w = 0 h = 0
    End Method
    
    ' Hides the control
    Method Hide( )
        If Not hwnd Then Return
        ShowWindow( hwnd, SW_HIDE )
    End Method
    
    ' Shows the control
    Method Show( )
        If Not hwnd Then Return
        ShowWindow( hwnd, SW_SHOW )
    End Method
    
    ' Resizes the control
    Method Resize( rw%, rh% )
        w = rw
        h = rh
        If Not hwnd Then Return
        MoveWindow( hwnd, x, y, w, h, True )
    End Method
    
    ' Positions the control
    Method Position( px%, py% )
        x = px
        y = py
        If Not hwnd Then Return
        MoveWindow( hwnd, x, y, w, h, True )
    End Method
    
    ' Sets the control's parent -- this is only useable before calling Build( )
    Method SetParent( p:IControl )
        If hwnd Then Return
        If p = parent Then Return
        If link Then link.Remove( ) link = Null
        parent = p
        If parent Then link = parent.child.AddLast( Self )
    End Method
    
    ' Forces the control to be redrawn
    ' You may, optionally, specify a region to redraw with ax, ay, aw, & ah
    Method Paint( ax%=-1,ay%=-1,aw%=-1,ah%=-1 )
        If Not hwnd Then Return
        If ax = -1 Or ay = -1 Or aw = -1 Or ah = -1 Then
            InvalidateRect( hwnd, Null, 0 )
        Else
            Local rect:Int[] = [ax,ay,aw,ah]
            InvalidateRect( hwnd, rect, 0 )
        EndIf
    End Method
End Type


' Window events
Const WindowClosed              =ControlEnumTop+1
Const WindowMaximized           =WindowClosed+1
Const WindowMinimized           =WindowMaximized+1
Const WindowFocusGained         =ControlFocusGained
Const WindowFocusLost           =ControlFocusLost
Const WindowMoved               =ControlMoved
Const WindowResized             =ControlResized
Const WindowEnumTop             =ControlEnumTop+32

Type IWindow Extends IControl
    ' Flags
        ' Misc.
        Const Resizable%=%1
        Const Titlebar%=%10
        Const SystemMenu%=%100
        Const ToolWindow%=%1000
        Const Visible%=%10000
        
        ' Buttons
        Const MinimizeButton%=%100000
        Const MaximizeButton%=%1000000
    
    Field caption$
    Field flags% = Resizable|Titlebar|SystemMenu|MinimizeButton|MaximizeButton
    
    Method Show( )
        If hwnd Then Super.Show( ) Return
        flags :| Visible
    End Method
    
    Method Hide( )
        If hwnd Then Super.Hide( ) Return
        flags = flags & (Not Visible)
    End Method
    
    Method HandleMessage%( msg%, wparam%, lparam% )
        Select msg
            Case WM_CLOSE
                EmitEvent( CreateEvent( WindowClosed, Self ) )
            Default
                Return Super.HandleMessage( msg, wparam, lparam )
        End Select
        Return 0
    End Method
    
    Method Build( )
        If Not hwnd Then
            Local ph%=0
            Local es% = WS_EX_CONTROLPARENT
            Local st% = WS_CLIPCHILDREN
            
            If parent Then
                ph = parent.hwnd
                st :| WS_CHILD
            EndIf
            
            If flags&Resizable Then st :| WS_SIZEBOX
            If flags&Titlebar Then st :| WS_CAPTION
            If flags&ToolWindow Then
                es :| WS_EX_TOOLWINDOW
            Else
                st :| WS_SYSMENU|WS_CAPTION
            EndIf
            If flags&MinimizeButton Then st :| WS_MINIMIZEBOX
            If flags&MaximizeButton Then st :| WS_MAXIMIZEBOX
            If flags&Visible Then st :| WS_VISIBLE
            
            hwnd = CreateWindowExW( es, iwndclass, caption, st, x, y, w, h, ph, 0, instance, Null )
        EndIf
        
        If Not hwnd Then Return
        For Local i:IControl = EachIn child
            i.Build( )
        Next
    End Method
End Type

Const ButtonPressed             =ControlAction
Const ButtonEnumTop             =WindowEnumTop+32
Type IButton Extends IControl
    Field caption$
    
    Method HandleMessage( msg%, wparam%, lparam% )
        Select msg
            Case WM_COMMAND
                EmitEvent( CreateEvent( ButtonPressed, Self ) )
            Default
                Return Super.HandleMessage( msg, wparam, lparam )
        End Select
        Return 0
    End Method
    
    Method Build( )
        If hwnd Then Return
        If Not parent Then Return
        hwnd = CreateWindowExW( 0, "Button", caption, WS_CHILD|WS_TABSTOP|BS_PUSHBUTTON, x, y, w, h, parent.hwnd, 0, instance, Null )
        Show( )
    End Method
End Type