PlanetSquires Forums

Support Forums => PlanetSquires Software => Topic started by: Frank Bruebach on September 26, 2026, 05:50:59 PM

Title: Own new oop Framework factory
Post by: Frank Bruebach on September 26, 2026, 05:50:59 PM
last weeks I have built a compact object‑oriented Windows GUI framework in FreeBASIC. It handles window  creation, message dispatching, menus, controls, and layout through a clean OOP event system.
A ControlFactory creates buttons, edits, combo boxes, and RichEdit controls. You added full
file I/O, including UTF‑8 with BOM support (later), plus native Open/Save dialogs. The framework wraps the WinAPI into a small, reusable architecture that behaves like a lightweight IDE foundation.

all work in progress.  ITS Not perfekt I know but a good start for coming more. Compiled with freebasic 1.10.1 win 10

regards, frank

'' ---------------------------------------------------------
' new freebasic OOP Window System – Factory
' 22-09-2026 by loewenherz / Frank b.
' ---------------------------------------------------------
' loadSaveFile fixed 24-09-2026
'
#Define WIN32_LEAN_AND_MEAN
#Include "windows.bi"
#Include Once "win/commctrl.bi"
#Include "win/commdlg.bi"

#Define MYWIN_CLASS_NAME "myWindowClass"
#Define CB_GETLBTEXTW &H149   ' Unicode LB_GETTEXT

#Define ID_BTN_OK      1001
#Define ID_BTN_EXIT    1002
#Define ID_FILE_OPEN   1003
#Define ID_FILE_SAVE   1004
#Define ID_HELP_ABOUT  1006
#Define ID_FILE_EXIT   1007
#Define ID_EDIT_HELLO  2001

Dim Shared As HMENU hMenu
Dim Shared As HMENU hFile
Dim Shared As HMENU hHelp
Dim Shared As Zstring * MAX_PATH g_szFile = "Untitled"

' ---------------------------------------------------------
' Control Basisklasse   
' ---------------------------------------------------------
Type ControlBase
    hwnd0   As HWND
    parent As HWND
    id     As Integer

    Declare Sub Create(className As String, text As String, _
                       style As UInteger, x As Integer, y As Integer, _
                       w As Integer, h As Integer)
    Declare Sub OnCommand()
End Type

Sub ControlBase.OnCommand()
End Sub

Sub ControlBase.Create(className As String, text As String, _
                       style As UInteger, x As Integer, y As Integer, _
                       w As Integer, h As Integer)

    hwnd0 = CreateWindowEx( _
        0, className, text, style, _
        x, y, w, h, _
        parent, Cast(HMENU, id), _
        GetModuleHandle(NULL), NULL)
End Sub

' ---------------------------------------------------------
' Edit Control
' ---------------------------------------------------------
Type EditControl Extends ControlBase
    Declare Constructor(parent As HWND, id As Integer)
    Declare Sub SetText(txt As String)
    Declare Function GetText() As String
End Type

Constructor EditControl(parent As HWND, id As Integer)
    This.parent = parent
    This.id = id
End Constructor

Sub EditControl.SetText(txt As String)
    SendMessage(This.hwnd0, WM_SETTEXT, 0, Cast(LPARAM, StrPtr(txt)))
End Sub

Function EditControl.GetText() As String
    Dim buffer As String * 1024
    GetWindowText(This.hwnd0, buffer, 1024)
    Return Trim(buffer)
End Function

' ---------------------------------------------------------
' ComboBox Control
' ---------------------------------------------------------
Type ComboBoxControl Extends ControlBase
    Declare Constructor(parent As HWND, id As Integer)
    Declare Sub AddItem(txt As String)
    Declare Function GetSelected() As String
End Type

Constructor ComboBoxControl(parent As HWND, id As Integer)
    This.parent = parent
    This.id = id
End Constructor

Sub ComboBoxControl.AddItem(txt As String)
    SendMessage(This.hwnd0, CB_ADDSTRING, 0, Cast(LPARAM, StrPtr(txt)))
End Sub

Function ComboBoxControl.GetSelected() As String
    Dim idx As Integer = SendMessage(This.hwnd0, CB_GETCURSEL, 0, 0)
    If idx < 0 Then Return ""
    Dim buffer As String * 256
    SendMessage(This.hwnd0, CB_GETLBTEXT, idx, Cast(LPARAM, StrPtr(buffer)))
    Return Trim(buffer)
End Function

' ---------------------------------------------------------
' RichEdit Control
' ---------------------------------------------------------
Type RichEditControl Extends ControlBase
    Declare Constructor(parent As HWND, id As Integer)
    Declare Sub SetText(txt As String)
    Declare Function GetText() As String
End Type

Constructor RichEditControl(parent As HWND, id As Integer)
    This.parent = parent
    This.id = id
    LoadLibrary("Msftedit.dll")
End Constructor

Sub RichEditControl.SetText(txt As String)
    SendMessage(This.hwnd0, WM_SETTEXT, 0, Cast(LPARAM, StrPtr(txt)))
End Sub

Function RichEditControl.GetText() As String
    Dim buffer As String * 4096
    GetWindowText(This.hwnd0, buffer, 4096)
    Return Trim(buffer)
End Function

' ---------------------------------------------------------
' Button Control
' ---------------------------------------------------------
Type ButtonControl Extends ControlBase
    Declare Constructor(parent As HWND, id As Integer)
    Declare Sub OnCommand()
End Type

Constructor ButtonControl(parent As HWND, id As Integer)
    this.parent = parent
    this.id = id
End Constructor

Sub ButtonControl.OnCommand()
    MessageBox(This.parent, "Button gedrückt!", "Event", MB_OK)
End Sub

' ---------------------------------------------------------
' Control Factory
' ---------------------------------------------------------
Type ControlFactory
    parent As HWND
    controls As Any Ptr Ptr
    count As Integer

    Declare Constructor(parent As HWND)
    Declare Destructor()

    Declare Function AddButton(text As String, id As Integer, _
                               x As Integer, y As Integer, w As Integer, h As Integer) As ButtonControl Ptr

    Declare Function AddEdit(text As String, id As Integer, _
                             x As Integer, y As Integer, w As Integer, h As Integer) As EditControl Ptr

    Declare Function AddCombo(id As Integer, _
                              x As Integer, y As Integer, w As Integer, h As Integer) As ComboBoxControl Ptr

    Declare Function AddRichEdit(id As Integer, _
                                 x As Integer, y As Integer, w As Integer, h As Integer) As RichEditControl Ptr
End Type

Constructor ControlFactory(parent As HWND)
    This.parent = parent
    This.count = 0
    This.controls = Callocate(0)
End Constructor

Destructor ControlFactory()
    For i As Integer = 0 To count - 1
        Delete Cast(ControlBase Ptr, controls[i])
    Next
    Deallocate(controls)
End Destructor

Function ControlFactory.AddButton(text As String, id As Integer, _
                                  x As Integer, y As Integer, w As Integer, h As Integer) As ButtonControl Ptr

    Dim btn As ButtonControl Ptr = New ButtonControl(parent, id)
    btn->Create("BUTTON", text, WS_CHILD Or WS_VISIBLE Or BS_PUSHBUTTON, _
                x, y, w, h)

    count += 1
    controls = Reallocate(controls, count * SizeOf(Any Ptr))
    controls[count - 1] = btn

    Return btn
End Function

Function ControlFactory.AddEdit(text As String, id As Integer, _
                                x As Integer, y As Integer, w As Integer, h As Integer) As EditControl Ptr

    Dim edt As EditControl Ptr = New EditControl(parent, id)
    edt->Create("EDIT", "", WS_CHILD Or WS_VISIBLE Or WS_BORDER Or ES_LEFT Or ES_AUTOHSCROLL, _
                x, y, w, h)

    edt->SetText(text)

    count += 1
    controls = Reallocate(controls, count * SizeOf(Any Ptr))
    controls[count - 1] = edt

    Return edt
End Function

Function ControlFactory.AddCombo(id As Integer, _
                                 x As Integer, y As Integer, w As Integer, h As Integer) As ComboBoxControl Ptr

    Dim cb As ComboBoxControl Ptr = New ComboBoxControl(parent, id)
    cb->Create("COMBOBOX", "", _
               WS_CHILD Or WS_VISIBLE Or WS_BORDER Or CBS_DROPDOWNLIST Or WS_VSCROLL, _
               x, y, w, h)

    count += 1
    controls = Reallocate(controls, count * SizeOf(Any Ptr))
    controls[count - 1] = cb

    Return cb
End Function

Function ControlFactory.AddRichEdit(id As Integer, _
                                    x As Integer, y As Integer, w As Integer, h As Integer) As RichEditControl Ptr

    Dim re As RichEditControl Ptr = New RichEditControl(parent, id)
    re->Create("RICHEDIT50W", "", _
    WS_CHILD Or WS_VISIBLE Or WS_BORDER Or WS_VSCROLL Or _
    ES_LEFT Or ES_MULTILINE Or ES_AUTOVSCROLL Or ES_WANTRETURN, _
    x, y, w, h)

    count += 1
    controls = Reallocate(controls, count * SizeOf(Any Ptr))
    controls[count - 1] = re

    Return re
End Function

' ---------------------------------------------------------
' Fensterklasse
' ---------------------------------------------------------
Type myWindows
    hhInstance As HINSTANCE
    nCmdShow As Integer
    title As ZString Ptr
    w As Integer
    h As Integer
    mhwnd As HWND

    btnOk As ButtonControl Ptr
    btnExit As ButtonControl Ptr
    editHello As EditControl Ptr
    factory As ControlFactory Ptr

    ' >>> WICHTIG: Diese zwei Felder waren bei dir NICHT vorhanden <<<
    cbFarbe As ComboBoxControl Ptr
    reText  As RichEditControl Ptr

    Declare Constructor()
    Declare Constructor(hInst As HINSTANCE, nCmdShow As Integer, _
                        ByRef title As ZString Ptr, w As Integer, h As Integer)

    Declare Function Create() As Integer
    Declare Sub Show()
    Declare Sub Loops()

    Declare Sub OnCreate(hwnd As HWND)
    Declare Sub OnCommand(hwnd As HWND, id As Integer)
    Declare Sub OnSize(hwnd As HWND, cw As Integer, ch As Integer)
    Declare Sub OnDestroy(hwnd As HWND)

    Declare Static Function WndProcs(hwnd As HWND, msg As UInteger, _
                                     wParam As WPARAM, lParam As LPARAM) As Integer
End Type

Constructor myWindows()
End Constructor

Constructor myWindows(hInst As HINSTANCE, nCmdShow As Integer, _
                      ByRef title As ZString Ptr, w As Integer, h As Integer)

    This.hhInstance = hInst
    This.nCmdShow = nCmdShow
    This.title = title
    This.w = w
    This.h = h
End Constructor

Function myWindows.Create() As Integer
    Dim wc As WNDCLASSEX
    wc.cbSize = SizeOf(WNDCLASSEX)
    wc.style = CS_HREDRAW Or CS_VREDRAW
    wc.lpfnWndProc = Cast(WNDPROC, @myWindows.WndProcs)
    wc.cbClsExtra = 0
    wc.cbWndExtra = 0
    wc.hInstance = hhInstance
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION)
    wc.hCursor = LoadCursor(NULL, IDC_ARROW)
    wc.hbrBackground = Cast(HBRUSH, COLOR_WINDOW + 1)
    wc.lpszMenuName = NULL
    wc.lpszClassName = StrPtr(MYWIN_CLASS_NAME)
    wc.hIconSm = wc.hIcon

    If RegisterClassEx(@wc) = 0 Then Return 0

    mhwnd = CreateWindowEx( _
        0, StrPtr(MYWIN_CLASS_NAME), title, _
        WS_OVERLAPPEDWINDOW Or WS_VISIBLE, _
        CW_USEDEFAULT, CW_USEDEFAULT, _
        w, h, _
        NULL, NULL, hhInstance, @This )

    If mhwnd = 0 Then Return 0
    Return 1
End Function

Sub myWindows.Show()
    ShowWindow(mhwnd, nCmdShow)
    UpdateWindow(mhwnd)
End Sub

Sub myWindows.Loops()
    Dim msg As MSG
    While GetMessage(@msg, NULL, 0, 0)
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    Wend
End Sub
'
'Function LoadTextFile(path As String) As String
    Dim s As String
    Open path For Input As #1
    While Not Eof(1)
        Dim lines As String
        Line Input #1, lines
        s &= lines & !"\n"
    Wend
    Close #1
    Return s
End Function

Sub SaveTextFile(path As String, txt As String)
    Open path For Output As #1
    Print #1, txt
    Close #1
End Sub

Function FileOpenDialog( byval hWnd as HWND ) as string

dim ofn as OPENFILENAME
dim filename as zstring * MAX_PATH+1

with ofn
.lStructSize = sizeof( OPENFILENAME )
.hwndOwner = hWnd
.hInstance = GetModuleHandle( NULL )
.lpstrFilter = strptr( !"All Files, (*.*)\0*.*\0Bas Files, (*.BAS)\0*.bas\0\0" )
.lpstrCustomFilter = NULL
.nMaxCustFilter = 0
.nFilterIndex = 1
.lpstrFile = @filename
.nMaxFile = sizeof( filename )
.lpstrFileTitle = NULL
.nMaxFileTitle = 0
.lpstrInitialDir = NULL
.lpstrTitle = @"File Open Test"
.Flags = OFN_EXPLORER or OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
.nFileOffset = 0
.nFileExtension = 0
.lpstrDefExt = NULL
.lCustData = 0
.lpfnHook = NULL
.lpTemplateName = NULL
end with

if( GetOpenFileName( @ofn ) = FALSE ) then
return ""
else
return filename
end if

end function

function FileSaveDialog( byval hWnd as HWND ) as string

dim ofn as OPENFILENAME
dim filename as zstring * MAX_PATH+1

with ofn
.lStructSize = sizeof( OPENFILENAME )
.hwndOwner = hWnd
.hInstance = GetModuleHandle( NULL )
.lpstrFilter = strptr( !"All Files, (*.*)\0*.*\0Bas Files, (*.BAS)\0*.bas\0\0" )
.lpstrCustomFilter = NULL
.nMaxCustFilter = 0
.nFilterIndex = 1
.lpstrFile = @filename
.nMaxFile = sizeof( filename )
.lpstrFileTitle = NULL
.nMaxFileTitle = 0
.lpstrInitialDir = NULL
.lpstrTitle = @"File Open Test"
.Flags = OFN_EXPLORER or OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
.nFileOffset = 0
.nFileExtension = 0
.lpstrDefExt = NULL
.lCustData = 0
.lpfnHook = NULL
.lpTemplateName = NULL
end with

if( GetSaveFileName( @ofn ) = FALSE ) then
return ""
else
return filename
end if

end function

Function LoadTextFileUTF8(path As String) As String
    Dim raw As String

    Open path For Binary As #1
    If LOF(1) > 0 Then
        raw = String(LOF(1), 0)
        Get #1, , raw
    End If
    Close #1

    ' UTF-8 BOM entfernen
    If Len(raw) >= 3 AndAlso _
       Asc(raw, 1) = &HEF AndAlso _
       Asc(raw, 2) = &HBB AndAlso _
       Asc(raw, 3) = &HBF Then
        raw = Mid(raw, 4)
    End If

    Return raw
End Function

Sub SaveTextFileUTF8(path As String, txt As String, withBOM As Boolean = True)
    Open path For Binary As #1
    If withBOM Then
        Dim bom As String = Chr(&HEF) + Chr(&HBB) + Chr(&HBF)
        Put #1, , bom
    End If
    Put #1, , txt
    Close #1
End Sub



' ---------------------------------------------------------
' Event-Methoden
' ---------------------------------------------------------
Sub myWindows.OnCreate(hwnd As HWND)

    factory = New ControlFactory(hwnd)

    btnOk      = factory->AddButton("OK", ID_BTN_OK, 10, 10, 80, 30)
    btnExit    = factory->AddButton("Exit", ID_BTN_EXIT, 100, 10, 80, 30)
    editHello  = factory->AddEdit("Hallo FreeBasic", ID_EDIT_HELLO, 10, 60, 200, 25)

    ' ComboBox
    cbFarbe = factory->AddCombo(3001, 10, 100, 200, 25)

cbFarbe->AddItem("Rot")
cbFarbe->AddItem("Grün")
cbFarbe->AddItem("Blau")
SendMessage(cbFarbe->hwnd0, CB_SETCURSEL, 0, 0)  ' "Rot" als Startauswahl

    ' RichEdit
    'reText = factory->AddRichEdit(4001, 10, 320, 300, 150)
    reText = factory->AddRichEdit(4001, 10, 140, 300, 150)
    reText->SetText("Bitte Text eingeben...")

    ' Menü
    hMenu = CreateMenu()

    hFile = CreatePopupMenu()
    AppendMenu(hFile, MF_STRING, ID_FILE_OPEN,  "Öffnen")
    AppendMenu(hFile, MF_STRING, ID_FILE_SAVE,  "Speichern")
    AppendMenu(hFile, MF_SEPARATOR, 0, 0)
    AppendMenu(hFile, MF_STRING, ID_FILE_EXIT,  "Exit")
    AppendMenu(hMenu, MF_POPUP, Cast(UINT_PTR, hFile), "Datei")

    hHelp = CreatePopupMenu()
    AppendMenu(hHelp, MF_STRING, ID_HELP_ABOUT, "Über...")
    AppendMenu(hMenu, MF_POPUP, Cast(UINT_PTR, hHelp), "Hilfe")

    SetMenu(hwnd, hMenu)
End Sub

Sub myWindows.OnCommand(hwnd As HWND, id As Integer)
    Select Case id

        Case ID_BTN_OK
            Dim txt As String = editHello->GetText()
            MessageBox(hwnd, txt, "Edit Inhalt", MB_OK)

        Case ID_BTN_EXIT
            DestroyWindow(hwnd)

        'Case ID_FILE_OPEN
        '    MessageBox(hwnd, "Öffnen gewählt.", "Menü", MB_OK)

        'Case ID_FILE_SAVE
        '    MessageBox(hwnd, "Speichern gewählt.", "Menü", MB_OK)

    Case ID_FILE_OPEN
    Dim f As String = FileOpenDialog(hwnd)
    If f <> "" Then
        Dim txt As String = LoadTextFile(f)
        reText->SetText(txt)
    End If

Case ID_FILE_SAVE
    Dim f As String = FileSaveDialog(hwnd)
    If f <> "" Then
        Dim txt As String = reText->GetText()
        SaveTextFile(f, txt)
    End If


        Case ID_FILE_EXIT
            DestroyWindow(hwnd)

        Case ID_HELP_ABOUT
            MessageBox(hwnd, "myWindowClass Beispiel 2026", "Über", MB_OK)

        Case 3001
            Dim sel As String = cbFarbe->GetSelected()
            MessageBox(hwnd, sel, "Combo Auswahl", MB_OK)

       

    End Select
End Sub

Sub myWindows.OnSize(hwnd As HWND, cw As Integer, ch As Integer)
    If btnOk <> 0 Then
        MoveWindow(btnOk->hwnd0, 10, ch - 40, 80, 30, TRUE)
    End If
   
    If btnExit <> 0 Then
        MoveWindow(btnExit->hwnd0, cw - 90, ch - 40, 80, 30, TRUE)
    End If
End Sub

Sub myWindows.OnDestroy(hwnd As HWND)
    If factory <> 0 Then
        Delete factory
        factory = 0
    End If
End Sub

' ---------------------------------------------------------
' WndProc Dispatcher
' ---------------------------------------------------------
Function myWindows.WndProcs(hwnd As HWND, msg As UInteger, _
                            wParam As WPARAM, lParam As LPARAM) As Integer

    Dim self As myWindows Ptr
    self = Cast(myWindows Ptr, GetWindowLongPtr(hwnd, GWLP_USERDATA))

    If msg = WM_NCCREATE Then
        Dim cs As CREATESTRUCT Ptr = Cast(CREATESTRUCT Ptr, lParam)
        self = Cast(myWindows Ptr, cs->lpCreateParams)
        SetWindowLongPtr(hwnd, GWLP_USERDATA, Cast(LONG_PTR, self))
        self->mhwnd = hwnd
    End If

    Select Case msg

        Case WM_CREATE
            If self <> 0 Then self->OnCreate(hwnd)
            Return 0

        Case WM_COMMAND
            If self <> 0 Then self->OnCommand(hwnd, LoWord(wParam))
            Return 0

        Case WM_SIZE
            If self <> 0 Then self->OnSize(hwnd, LoWord(lParam), HiWord(lParam))
            Return 0

        Case WM_DESTROY
            If self <> 0 Then self->OnDestroy(hwnd)
            PostQuitMessage(0)
            Return 0

    End Select

    Return DefWindowProc(hwnd, msg, wParam, lParam)
End Function

' ---------------------------------------------------------
' WinMain
' ---------------------------------------------------------
Function WinMain(hInst As HINSTANCE, hPrev As HINSTANCE, _
                 cmd As ZString Ptr, nCmdShow As Integer) As Integer

    Dim win As myWindows = myWindows(hInst, nCmdShow, @"Lion_OOP Window Factory version 0.4", 800, 600)

    If win.Create() Then
        win.Show()
        win.Loops()
    End If

    Return 0
End Function

End WinMain(GetModuleHandle(NULL), NULL, Command$, SW_SHOWDEFAULT)
' ends

Next Update will come soon so you can Understand more how to handle a factory.bi file
Title: Re: Own new oop Framework factory
Post by: Paul Squires on September 27, 2026, 07:17:39 AM
Congratulations on your code! I bet that you learned a lot while designing and writing this code. Writing a GUI framework involves a lot of different concepts especially since you designed it using OOP.
Title: Re: Own new oop Framework factory
Post by: hajubu on September 27, 2026, 09:22:55 AM
Hi Frank,

Fine work ! - "Klasse" !

please do not forget the German Umlaute (ÄäÖöÜü...etc) are not regularly displayed now.
Thanks

P.S:
 I saw also your Windows Class Demo 3 as of 2026-09-14.
 In this Demo3 the Messagebox could be easily adapted  with include the older Afx\CWStr.inc() and ...
Quoteapp. line 97
MessageBoxExW(hwnd, CWSTR("!German Umlaut! OK gedrückt!", CP_UTF8), "Info", MB_OK, SUBLANG_NEUTRAL)

Here in the OOP construct it will take more effort.


Thanks
Title: Re: Own new oop Framework factory
Post by: Paul Squires on September 27, 2026, 02:10:13 PM
Quote from: hajubu on September 27, 2026, 09:22:55 AMplease do not forget the German Umlaute (ÄäÖöÜü...etc) are not regularly displayed now.
Unfortunately used "string" and "zstring" which will not display unicode characters. In my programs I use José's CWindow and related functions (that are dpi aware) to ensure that all languages can be displayed.