Simple WinAPI GUI library

User projects written in or related to FreeBASIC.
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Simple WinAPI GUI library

Post by Lothar Schirm »

The project consists of a small WinAPI library which can be used to create Window GUI applications in a simple way. It is based on an the "Easy Windows API Tutorial" http://freebasic.net/forum/viewtopic.php?f=7&t=13451 by bojan.dosen. Some code examples will help to understand the stuff.

Download at https://www.freebasic-portal.de/downloa ... k-358.html.

Latest update on January 07, 2018

Here are some examples:

Example 1:
Window with text editor (multiline editbox) and a button to copy text from the editor to the console window:

Code: Select all

#Include "WinGUI.bi"

Dim As HWND Window_Main, Editor_txt, Button_Ok
Dim As MSG msg

'Create a window with a text editor and a button:
Window_Main = Window_New	(100, 100, 400, 400, "I am a beautiful window!")
Var Label_txt = Label_New	(10, 10, 150, 20, "Enter a text:",, Window_Main)
Editor_txt = Editor_New		(10, 40, 360, 250, "",, Window_Main)
Button_Ok = Button_New		(160, 300, 60, 20, "Ok",, Window_Main)

'Event loop:
Do
	WaitEvent(Window_Main, msg)
	Select Case msg.hwnd
		Case Button_Ok
			If msg.message = WM_LBUTTONDOWN Then
				'Print text from editor on the console
				Cls
				Print EditBox_GetText(Editor_txt)
			End If	
	End Select
Loop Until Window_Event_Close(Window_Main, msg)

End
Example 2:
Window with menu and a "Close" button

Code: Select all

#Include "WinGUI.bi"

Dim As HWND Window_Main, Button_Close
Dim As HMENU hMenu, hFile, hHelp
Dim As MSG msg

'Create a window:
Window_Main = Window_New	(100, 100, 500, 500, "Test menu")
	
'Create menu and nenu headers:
hMenu = CreateMenu()
hFile = CreateMenu()
hHelp = CreateMenu()

'Create menu titles:
MenuTitle(hMenu, hFile, "File")
MenuTitle(hMenu, hHelp, "Help")

'Create menu items:
MenuItem(hFile, 1, "New")
MenuItem(hFile, 2, "Open")
MenuItem(hFile, 3, "Save")
MenuItem(hFile, 4, "Exit")
MenuItem(hHelp, 5, "Info")

'Assign menu to the window:
SetMenu(Window_Main, hMenu)

'Create a "Close" button:
Button_Close = Button_New(200, 200, 80, 20, "Close",, Window_Main)
	
'Event loop:
Do
	WaitEvent(Window_Main, msg)
  
	Select Case msg.hwnd
	
  Case Window_Main
    Select Case msg.message
    Case WM_COMMAND ' Menu commands
      Select Case msg.wParam
      Case 1
        Print "New"
      Case 2
        Print "Open"
      Case 3
        Print "Save"
      Case 4
        Print "Exit"
      Case 5
        Messagebox(0, "Test of menu functions", "Info", 0)
      End Select
    End Select
    
  Case Button_Close
    If msg.message = WM_LBUTTONDOWN Then Exit Do
    
  End Select
  
Loop Until Window_Event_Close(Window_Main, msg)

End
Example 3:
A simple calculator: Write two numbers into two different edit boxex, click a button "+", "-", "*" or "/" and show the result in a third edit box

Code: Select all

#Include "WinGUI.bi"

Dim Shared As MSG msg
Dim Shared As HWND Window_Main, Edit_a, Edit_b, Edit_c, _
							Button_Plus, Button_Minus, Button_Mult, Button_Div
														

Sub CreateWindow_Main()
	'Main window
	
	Window_Main = Window_New	(200, 200, 300, 150, "Simple Calculator")
	Var Label_a = Label_New		(10, 10, 90, 20, "Number 1:",, Window_Main)  
	Edit_a = EditBox_New			(100, 10, 100, 20, "0",, Window_Main)	
	Var Label_b = Label_New		(10, 40, 90, 20, "Number 2:",, Window_Main)
	Edit_b = EditBox_New			(100, 40, 100, 20, "0",, Window_Main)	
	Var Label_c = Label_New		(10, 70, 90, 20, "Result:",, Window_Main)
	Edit_c = EditBox_New			(100, 70, 100, 20, "0", WS_TABSTOP Or ES_READONLY, Window_Main)
	Button_Plus = Button_New	(210, 10, 20, 20, "+",, Window_Main)
	Button_Minus = Button_New	(240, 10, 20, 20, "-",, Window_Main)
	Button_Mult = Button_New	(210, 40, 20, 20, "*",, Window_Main)
	Button_Div = Button_New		(240, 40, 20, 20, "/",, Window_Main)
	
End Sub


Sub Calculate(ByVal method As String)
	'Get a and b from edit boxes, calculate and set result c into edit box
	
	Dim As Single a, b, c
	
	a = Val(EditBox_GetText(Edit_a))
	b = Val(EditBox_GetText(Edit_b))
	
	Select Case method
		Case "+"
			c = a + b
		Case "-"
			c = a - b
		Case "*"
			c = a * b
		Case "/"
			c = a / b
	End Select
	
	EditBox_SetText(Edit_c, Str(c))
	
End Sub


'Callback functions:

Sub Button_Plus_Event()
	Calculate("+")
End Sub

Sub Button_Minus_Event()
	Calculate("-")
End Sub
	
Sub Button_Mult_Event()
	Calculate("*")
End Sub	
	'
Sub Button_Div_Event()
	Calculate("/")
End Sub


'Main:

CreateWindow_Main()

Do
	WaitEvent(Window_Main, msg)

	Select Case msg.hwnd
		Case Button_Plus
			If msg.message = WM_LBUTTONDOWN Then Button_Plus_Event()
		Case Button_Minus
			If msg.message = WM_LBUTTONDOWN Then Button_Minus_Event()
		Case Button_Mult
			If msg.message = WM_LBUTTONDOWN Then Button_Mult_Event()
		Case Button_Div
			If msg.message = WM_LBUTTONDOWN Then Button_Div_Event()
	End Select
	
Loop Until Window_Event_Close(Window_Main, msg)	
	
End
Example 4:
Window with a listbox and controls to add, delete, insert or replace items

Code: Select all

#Include "WinGUI.bi"

Dim As MSG msg
Dim As HWND WinMain, List, Edit_Index, Edit_SelText, Edit_Text, Button_Add, _
						Button_Delete, Button_Insert, Button_Replace
Dim As Integer i, index

'Create window:
WinMain = Window_New					(100, 100, 400, 400, "Test Listbox")
List = ListBox_New						(20, 20, 150, 300,, WinMain)
Var Label_Index = Label_New		(200, 50, 150, 20, "Selected index:",, WinMain)
Edit_Index = EditBox_New			(200, 70, 150, 20, "", WS_TABSTOP Or ES_CENTER Or ES_READONLY, WinMain)
Var Label_SelText = Label_New	(200, 100, 150, 20, "Selected text:",, WinMain)
Edit_SelText = EditBox_New		(200, 120, 150, 20, "", WS_TABSTOP Or ES_READONLY, WinMain)
Button_Delete = Button_New		(200, 150, 150, 20, "Delete selected item",, WinMain)
Edit_Text = EditBox_New				(200, 200, 150, 20, "Type text here",, WinMain)
Button_Add = Button_New				(200, 230, 150, 20, "Add to list",, WinMain)
Button_Insert = Button_New		(200, 260, 150, 20, "Insert",, WinMain)
Button_Replace = Button_New		(200, 290, 150, 20, "Replace",, WinMain)


For i = 0 To 20
	ListBox_AddString(List, "Item number " + Str(i))
Next
ListBox_SetCurSel(List, 0)

Do
	WaitEvent(WinMain, msg)
	If msg.message = WM_LBUTTONDOWN Then
		Select Case msg.hwnd
			Case List
				index = ListBox_GetCurSel(List)
				EditBox_SetText(Edit_Index, Str(index))
				EditBox_SetText(Edit_SelText, ListBox_GetText(List, index))
			Case Button_Add
				ListBox_AddString(List, EditBox_GetText(Edit_Text))
			Case Button_Delete
				ListBox_DeleteString(List, index)
			Case Button_Insert
				ListBox_InsertString(List, index, EditBox_GetText(Edit_Text))
			Case Button_Replace
				ListBox_ReplaceString(List, index, EditBox_GetText(Edit_Text)) 
		End Select
	End If
Loop Until Window_Event_Close(WinMain, msg)

End
Last edited by Lothar Schirm on Jan 07, 2018 15:55, edited 9 times in total.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Simple WinAPI GUI library

Post by MrSwiss »

@Lothar,

looking quickly through WinDialog.bi, at the very End you have:
BGRtoRGB() ... and there is a conversion Fault:

Code: Select all

Function BGRtoRGB(ByVal colour As ULong) As ULong
	'Converts a BGR color format to RGB
	
	Dim As String BGRString, RGBString
	
	BGRString = Hex(colour, 6)
	RGBString = "&H" + Right(BGRString, 2) + Mid(BGRString, 3, 2) + Left(BGRString, 2)
	
	Return ValUInt(RGBString)  ' corrected: this gives ULong 32 bit (ValULng = ULongInt 64bit) 
	
End Function
I've already raised this Issue ... no solution from the Dev's (they know it's misleading).
However, in FB-Doc, it is correctly described, thanks to fxm ...
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Simple WinAPI GUI library

Post by Lothar Schirm »

Ok, I still use the German FreeBasic Reference from FreeBasic Portal, dated September 2014. Seems to be outdated or wrong. In the new FB manual, it is explained as you say. I will correct the fault in my own WinDialogs.bi so that it will be corrected in the next update. For the moment, I think, it is a minor problem, I tested "Paint.bas" in the download package with the colorpicker, and it works.
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

Hi Lothar

Thank you for updating your Simple WinAPI GUI library.

I note that there have been 135 views in 3 days. Not bad! As Lothar has stated, it is a simple WinAPI library that only does the basics of "boxes" in a GUI. This is very good for setting up on-screen forms and the syntax is simple to follow. I would encourage Windows users to try it out.

Ray
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Simple WinAPI GUI library

Post by Lothar Schirm »

Thank you, Ray. I added some examples in the first post.
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

I have been looking at the Calculator example that Lothar included with his WinGUI library.

I would like the input to be character by character as in a normal electronic calculator.

I would like to do a check on a character by character basis on the input to the editboxes that checks for numbers 0-9, "-" and "."

I would like all other characters to be excluded and immediately deleted by the backspace char on input.

I have recently come across a method to do this in Liberty Basic and have tried to convert it to Freebasic, with no success.

The Liberty Basic code is :

Code: Select all

nomainwin  ' this prevents the main window opening
textbox #main.textbox, 10, 50, 100, 25  'sets up a textbox.  #main.textbox is its handle
WindowWidth = 350  ' window size
WindowHeight = 150

open "Filtered Numeric Input" for window as #main  ' opens window
#main.textbox "!contents? txt$"    'gets the text in the textbox
#main "trapclose [quit]"     'closes the window

[CheckInput]       ' label
timer 0

    #main.textbox "!contents? txt$"   'text in textbox
     oldtxt$ = txt$    ' make text the oldtext
    txt$ = num$(txt$)  ' gets new text from function num$(text$) which does the checking
    if oldtxt$ <> txt$ then
        #main.textbox txt$      ' if old text is not the same as the new text then use new text
        handle = hWnd(#main.textbox)   ' get the handle of the textbox
        pos = len(txt$)    ' position of character is the length of the current text
        calldll #user32, "SendMessageA", _   ' call EM_SETSEL
            handle as ulong, _
            _EM_SETSEL as long, _
            pos as long, _
            pos as long, _
            result as void
    end if

timer 300, [CheckInput]  ' short delay then last char is deleted if not 0-9, - or .
wait   ' wait for event

' Function to check that character input is 0-9, - or .
function num$(d$)
    for i=1 to len(d$)
        a=asc(mid$(d$,i,1))
        if a = 45 or a = 46 or a>47 and a<58 then num$=num$+chr$(a)
    next
end function

[quit]
close #main   ' close window
end
I have tried to modify the above for Freebasic, but get loads of error messages:

Code: Select all

#Include "WinGUI.bi"

Dim As HWND Window_Main, Editor_txt, Button_Ok, Edit_1
Dim As MSG msg
dim as string txt, num(), oldtxt
'dim as integer i, a

function num(txt)
dim as integer i, a
DIM AS STRING num
    for i = 1 to len(txt)
        a = asc(mid(txt,i,1))
        if a = 45 or a = 46 or a>47 and a<58 then num=num+chr(a)
    next
end function

'Create a window with an Editbox and a button:
Window_Main = Window_New	(100, 100, 400, 400, "Numeric Entry Filter!")
Var Label_txt = Label_New	(10, 10, 150, 20, "Enter numbers:",, Window_Main)
Edit_1 = EditBox_New			(150, 10, 100, 20, "0",, Window_Main)
Button_Ok = Button_New		(160, 300, 60, 20, "Ok", Window_Main)

do
sleep 10

    text = EditBox_GetText(Edit_1)
    oldtxt=txt
    txt = num(txt)
    if oldtxt <> txt then
        text = EditBox_GetText(Edit_1)
        handle = hWnd(Edit_1)
        pos = len(txt)
        'calldll #user32, "SendMessageA", _
        if "SendMessageA", _
            handle as ulong, _
            EM_SETSEL as long, _
            pos as long, _
            pos as long, _
            result as void
        end if    
    end if

sleep 300
Loop Until Window_Event_Close(Window_Main, msg)	
Can anyone point me in the right direction?
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Simple WinAPI GUI library

Post by Lothar Schirm »

Hi Ray, I tried to change your code. It produces no errors and shows a nice window, but I do not know whether I understood well want you want to do. Here is what I have done, with some remarks:

Code: Select all

#Include "WinGUI.bi"

Dim As HWND Window_Main, Editor_txt, Button_Ok, Edit_1
Dim As MSG msg
dim as string txt, text, oldtxt		'<*** Code changed: "(num()" deleted, is a function name (see below), "text" added
'dim as integer i, a

function num(ByVal txt As String) As String		'<*** Code changed
dim as integer i, a
DIM AS STRING num1		'<*** Code changed (duplicated definition, "num" renamed to num1														
    for i = 1 to len(txt)
        a = asc(mid(txt,i,1))
        if a = 45 or a = 46 or a>47 and a<58 then num1 = num1 + chr(a)	'<*** Code changed
    next
    return num1		'<*** Code added - shall this function return num1?
end function

'Create a window with an Editbox and a button:
Window_Main = Window_New   (100, 100, 400, 400, "Numeric Entry Filter!")
Var Label_txt = Label_New   (10, 10, 150, 20, "Enter numbers:",, Window_Main)
Edit_1 = EditBox_New         (150, 10, 100, 20, "0",, Window_Main)
Button_Ok = Button_New      (160, 300, 60, 20, "Ok", Window_Main)

do											
	WaitEvent(msg)			'<*** You need an event loop here!
	If msg.message = WM_LBUTTONDOWN And msg.hwnd = Button_Ok Then
		'sleep 10

    text = EditBox_GetText(Edit_1)
    oldtxt=txt
    txt = num(txt)
    if oldtxt <> txt then
        text = EditBox_GetText(Edit_1)
        'handle = hWnd(Edit_1)						'<*** Edit_1 is a hwnd!
        'pos = len(txt)										'<*** pos has an other meaning in FreeBasic!
        'calldll #user32, "SendMessageA", _
        if SendMessage (Edit_1, EM_SETSEL, len(txt), len(txt)+1) Then		'<*** Code changed - start value and end value shoul be diiferent, is this ok?
            '*** something should happen here ***
        end if   
    end if

		'sleep 300
	End If
Loop Until Window_Event_Close(Window_Main, msg)   
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

Lothar

I've replied once, but no sign of my post, so I'll do it again.

What I am trying to do is to enter numbers calculator style one by one and each character is immediately individually checked to ensure that it is a number or "-" or "."

If it is OK then the number is built up by appending to the last character(s) entered. If it isn't, it is immediately deleted and then you can carry on typing numbers until your correct number is entered.

So, if I want to enter 123, I would enter:
1 OK, then 2 OK, but now I mistype and enter "e" instead of "3". Because it's not a number, it is immediately deleted. I then carry on entering 3 which is OK, making my correct number 123.

I hope that makes it a bit clearer.
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Simple WinAPI GUI library

Post by Lothar Schirm »

You need to a set a Windows timer to 300 milliseconds, and you must use the WM_TIMER message in order to trigger the checking of the text in the edit box. An other example how to use the timer: see example "Progress Bar" in http://freebasic.net/forum/viewtopic.php?f=7&t=13451. This is my solution for your problem:

Code: Select all

#Include "WinGUI.bi"

Dim As HWND Window_Main, Button_Ok, Edit_1
Dim As MSG msg
dim As String txt, oldtxt
Dim As Integer pos0	


Function num(ByVal txt As String) As String
	'Function to check that character input is 0-9, - or .
	
	Dim As String num1
	Dim As Integer i, a
															
	For i = 1 To Len(txt)
		a = Asc(Mid(txt,i,1))
		If a = 45 Or a = 46 or a > 47 and a < 58 then num1 = num1 + Chr(a)
	Next
	
	Return num1

End Function


'Create a window with an Editbox and a button:
Window_Main = Window_New   (100, 100, 400, 400, "Numeric Entry Filter!")
Var Label_txt = Label_New  (10, 10, 150, 20, "Enter numbers:",, Window_Main)
Edit_1 = EditBox_New       (150, 10, 100, 20, "0",, Window_Main)
Button_Ok = Button_New     (160, 300, 60, 20, "Ok", Window_Main)

'Set timer to 300 miliseconds:
SetTimer(Window_Main, 0, 300, 0 )

Do
	WaitEvent(msg)
	Select Case msg.message
		Case WM_TIMER
			'Check contents of the edit box every 300 millisecinds
			txt = EditBox_GetText(Edit_1)	'get text from edit box
			oldtxt = txt		'make text the oldtext
			txt = num(txt)  'gets new text from function num(txt) which does the checking
			If oldtxt <> txt Then
				EditBox_SetText(Edit_1, txt)	'if old text is not the same as the new text then use new text
				pos0 = Len(txt)	'position of character is the length of the current text
				SendMessage(Edit_1, EM_SETSEL, pos0, pos0)
			End If
		Case WM_LBUTTONDOWN
			If msg.hwnd = Button_Ok Then Print EditBox_GetText(Edit_1)  'print text to console 
	End Select
Loop Until Window_Event_Close(Window_Main, msg)

End
In the Function num you could also make sure that "-" is only allowed at the beginning (as sign "minus") and that "." is not allowed at the end and not more than once.
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

Lothar Schirm wrote:You need to a set a Windows timer to 300 milliseconds, and you must use the WM_TIMER message in order to trigger the checking of the text in the edit box. An other example how to use the timer: see example "Progress Bar" in http://freebasic.net/forum/viewtopic.php?f=7&t=13451. This is my solution for your problem:
Lothar, you are a marvel. Your solution is absolutely smack on!

I agree more error checking needs to be done, but you have solved the basic requirement.

Thank you very much

Ray
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

Lothar

Just a query that you may have come across before.

For an IDE, I like FBIde and CSED_FB. However, when I run the code for the text entry, the console window comes up with FBIde so the number entered can be printed to it, but with CSED_FB, the console window does not appear. Do you know if this is a known issue?

Ray
Lothar Schirm
Posts: 436
Joined: Sep 28, 2013 15:08
Location: Germany

Re: Simple WinAPI GUI library

Post by Lothar Schirm »

I am not familiar with CSED. Do you use compiler option -s console? This should force the compiler to open the console window. I prefer to use Geany, because it has comfortable autocompletec functions, and the console window remains open when the program is finished. This has the advantage that runtime errors will be displayed on the console window until the user presses a key. FBIde can be configured in the same way (see http://www.freebasic.net/forum/viewtopi ... =2&t=19950, but CSED and FBEdit not.
RNBW
Posts: 267
Joined: Apr 11, 2015 11:06
Location: UK

Re: Simple WinAPI GUI library

Post by RNBW »

Hi Lothar

I thought I got it via the German portal, so I thought perhaps you may have tried it.

However, I've solved it. I presumed when I clicked the radio button for GUI (which is the default) that would do the same as FBIde. However, I tried clicking the radio button for console instead and that worked. It's not very clear, but it does now seem to work.
datwill310
Posts: 355
Joined: May 29, 2015 20:37

Re: Simple WinAPI GUI library

Post by datwill310 »

Lothar Schirm wrote:The project consists of a small WinAPI library which can be used to create Window GUI applications in a simple way.
Hi! It looks fantastic, downloaded it, and I'm impressed! However, there might be a slight issue. See, in WinGUI.bi:

Code: Select all

#include once "windows.bi"
#include "win/commctrl.bi" 'required for TrackBar and ProgressBar
InitCommonControls()
const Dialog_Box = "#32770" 'system class dialog box
Shouldn't "win/commctrl.bi" be included once? Like this?

Code: Select all

#include once "windows.bi"
#include once "win/commctrl.bi" 'required for TrackBar and ProgressBar
InitCommonControls()
const Dialog_Box = "#32770" 'system class dialog box
With such a small problem, it's not going to cause much trouble, and if the programmer comes across suplicated definition errors, they're going to edit the header file themselves appropriately. But if the library is updated a lot and stuff, it could get annoying.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Simple WinAPI GUI library

Post by MrSwiss »

datwill310 wrote:Shouldn't "win/commctrl.bi" be included once? Like this?
"Once" qualifier is only needed if: another (related .bi), might try to load (the same #include) again.
This is clearly not the case here ... (exception is: "Windows.bi").

WinGUI is split into two parts: there are two .bi Files:
  • WinGUI.bi
  • WinDialogs.bi
Only the later requires the inclusion of #Include Once "win/commdlg.bi", which is "made" there.
It is no problem to include both (at the same time), because:

there are no conflicting #Include-Statements present.
Post Reply