Code: Select all
;====================================================================
; NumericInputBox() - custom input requester for decimal digits
;
; cross-platform masking routines by Keya & Shardik - Thank you!
; original posted code available in the following forum thread:
;
;====================================================================
; http://forums.purebasic.com/english/viewtopic.php?t=67986&p=503517
;====================================================================
;
; tested & working on Windows 10 running PureBasic v5.62 (x64)
;
; by TI-994A - free to use, improve, share...
;
; 10th May 2018
;====================================================================
;==================
; masking routines
;==================
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
ImportC ""
GetControlData(ControlRef.L, ControlPartCode.L, TagName.L, BufferSize.L, *Buffer, *ActualSize)
SetControlData(ControlRef.L, ControlPartCode.L, TagName.L, BufferSize.L, *Buffer)
EndImport
#kControlEditTextPart = 5
#kControlEditTextSelectionTag = $73656C65 ;'sele'
Structure ControlEditTextSelectionRec
SelStart.W
SelEnd.W
EndStructure
CompilerElse
Global NSRangeZero.NSRange
Procedure.i TextEditor(Gadget.i)
Protected TextField.i = GadgetID(Gadget)
Protected Window.i = CocoaMessage(0, TextField, "window")
ProcedureReturn CocoaMessage(0, Window, "fieldEditor:", #YES, "forObject:", TextField)
EndProcedure
CompilerEndIf
CompilerEndIf
Procedure SetStringGadgetSelection(GadgetID.i, Start.i, Length.i=0)
SetActiveGadget(gadgetid)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
SendMessage_(GadgetID(GadgetID), #EM_SETSEL, Start-1, Start-1)
CompilerCase #PB_OS_Linux
gtk_editable_select_region_(GadgetID(gadgetid), Start-1, Start-1)
CompilerCase #PB_OS_MacOS
CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
Protected TextSelection.ControlEditTextSelectionRec
TextSelection\selStart = Start - 1
TextSelection\selEnd = Start -1
SetControlData(GadgetID(GadgetID), #kControlEditTextPart,
#kControlEditTextSelectionTag, SizeOf(ControlEditTextSelectionRec), @TextSelection)
CompilerElse
Protected Range.NSRange\location = Start - 1 : Range\length = 0
CocoaMessage(0, TextEditor(GadgetID), "setSelectedRange:@", @Range)
CompilerEndIf
CompilerEndSelect
EndProcedure
Procedure.i GetCaretPosition(GadgetID.i)
Protected Start.i = 0
Protected Stop.i = 0
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
SendMessage_(GadgetID(GadgetID.i), #EM_GETSEL, @Start.i, @Stop.i)
ProcedureReturn Stop + 1
CompilerCase #PB_OS_Linux
Stop = gtk_editable_get_position_(GadgetID(gadgetid))
ProcedureReturn Stop + 1
CompilerCase #PB_OS_MacOS
CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
Protected TextSelection.ControlEditTextSelectionRec
GetControlData(GadgetID(GadgetID), #kControlEditTextPart, #kControlEditTextSelectionTag,
SizeOf(ControlEditTextSelectionRec), @TextSelection.ControlEditTextSelectionRec, 0)
ProcedureReturn TextSelection\End + 1
CompilerElse
Protected Range.NSRange
CocoaMessage(@Range, TextEditor(GadgetID), "selectedRange")
ProcedureReturn Range\location + Range\length + 1
CompilerEndIf
CompilerEndSelect
EndProcedure
Procedure RestrictChars(*pchr.Ascii, slen)
If slen
*pout.Ascii = *pchr
For i = 1 To slen
Select *pchr\a
Case '0' To '9'
*pchr+SizeOf(Character): *pout+SizeOf(Character)
Case '.':
dotcnt+1
If dotcnt=1
*pchr+SizeOf(Character): *pout+SizeOf(Character)
Else
Goto BadChar
EndIf
Default:
BadChar:
*pchr+SizeOf(Character): changed=1
EndSelect
*pout\a = *pchr\a
Next
EndIf
ProcedureReturn changed
EndProcedure
Procedure StringFilter(gadget)
Static Changing
If Not Changing
Changing=1
sTxt$ = GetGadgetText(gadget)
lastcaretpos = GetCaretPosition(gadget)
If RestrictChars(@sTxt$, Len(sTxt$))
SetGadgetText(gadget, sTxt$)
SetStringGadgetSelection(gadget, lastcaretpos-1)
EndIf
Changing=0
EndIf
EndProcedure
;====================
; input box routines
;====================
Procedure InputBoxHandler()
Select Event()
Case #PB_Event_CloseWindow
w = EventWindow()
DisableWindow(GetWindowData(w), #False)
CloseWindow(w)
Case #PB_Event_Gadget
g = EventGadget()
w = GetGadgetData(g)
If GadgetType(g) = #PB_GadgetType_Button
DisableWindow(GetWindowData(w), #False)
CloseWindow(w)
Else
StringFilter(g)
SetGadgetText(w, GetGadgetText(g))
EndIf
EndSelect
EndProcedure
Procedure InputBox(parentWindow, parentInput1, parentInput2, x = 0, y = 0, width = 300, height = 160)
inputBoxWindow = OpenWindow(#PB_Any, x, y, width, height, "Numeric InputBox",
#PB_Window_SystemMenu | #PB_Window_WindowCentered,
WindowID(parentWindow))
inputBoxLabel = TextGadget(#PB_Any, 50, 15, 200, 30, "Allowed chars: 0-9 and one '.'")
inputBoxNumeric1 = StringGadget(#PB_Any, 50, 40, 200, 24, "")
inputBoxNumeric2 = StringGadget(#PB_Any, 50, 70, 200, 24, "")
inputBoxButton = ButtonGadget(#PB_Any, 50, 100, 200, 30, "OK")
SetWindowData(inputBoxWindow, parentWindow)
SetGadgetData(inputBoxNumeric1, parentInput1)
SetGadgetData(inputBoxNumeric2, parentInput2)
SetGadgetData(inputBoxButton, inputBoxWindow)
BindGadgetEvent(inputBoxButton, @InputBoxHandler())
BindGadgetEvent(inputBoxNumeric1, @InputBoxHandler(), #PB_EventType_Change)
BindGadgetEvent(inputBoxNumeric2, @InputBoxHandler(), #PB_EventType_Change)
BindEvent(#PB_Event_CloseWindow, @InputBoxHandler(), inputBoxWindow)
DisableWindow(parentWindow, #True)
EndProcedure
;===============
; usage example
;===============
tFlags = #PB_Text_Center | #PB_Text_Border
wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
mainWindow = OpenWindow(#PB_Any, 0, 0, 400, 300, "Custom Numeric InputBox", wFlags)
clockDisplay = TextGadget(#PB_Any, 0, 20, 380, 20, "Clock Display", #PB_Text_Right)
numericValue1 = TextGadget(#PB_Any, 90, 70, 220, 30, "Float Value 1", tFlags)
numericValue2 = TextGadget(#PB_Any, 90, 110, 220, 30, "Float Value 2", tFlags)
popInputBox = ButtonGadget(#PB_Any, 90, 160, 220, 30, "INPUT REQUIRED VALUES")
SetGadgetColor(numericValue1, #PB_Gadget_BackColor, RGB(255, 150, 250))
SetGadgetColor(numericValue2, #PB_Gadget_BackColor, RGB(150, 250, 255))
AddWindowTimer(mainWindow, clock, 1000)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Select EventWindow()
Case mainWindow
appQuit = 1
EndSelect
Case #PB_Event_Timer
Select EventTimer()
Case clock
SetGadgetText(clockDisplay, FormatDate("%hh:%ii:%ss", Date()))
EndSelect
Case #PB_Event_Gadget
Select EventGadget()
Case popInputBox
InputBox(mainWindow, numericValue1, numericValue2)
EndSelect
EndSelect
Until appQuit
The masking and input box routines are fully portable and could be extracted and included as separate
files for use in any project. The input box routines could also work independently if the decimal-digit sub-classing is not required. The code is easily modifiable to accommodate any number of inputs, as required.
The input values would automatically be inserted into the respective placeholders.
section, which accepts multiple text and decimal-digit inputs.
Hope it helps.