SDI Example with Window Key Navigation
Posted: Sat Jan 22, 2011 9:15 am
It involves some work to add keyboard navigation to window gadgets, but it sure is a lot more friendly for users, especially for visually impaired people. For example, many applications support arrow left/right keys on radio buttons and buttons at the bottom of the window. Below is an example. It is a version of my library Notation Window. You can also see how I call windows in an SDI environment. In addition you can see how I cross-platform limit string input.
Code: Select all
; =============================================================================
; SDI Example with Window key navigation
; By Frarth
; =============================================================================
;{ - stuff added to my PB extensions library
Enumeration
#Window_Key_Return
#Window_Key_Escape
#Window_Key_Left
#Window_Key_Right
EndEnumeration
#Gadget_Disabled = 1
#Gadget_Enabled = 0
Global Key_Return_Off.b
Global Key_Escape_Off.b
Global Key_Left_Off.b
Global Key_Right_Off.b
Procedure LimitStringGadget(Gadget.i, Limit.i)
Static Text.s
Protected CurrentText.s = GetGadgetText(Gadget)
If Len(CurrentText) > Limit
SetGadgetText(Gadget, Text)
Else
Text = CurrentText
EndIf
EndProcedure
Procedure XDisableGadget(Gadget.i, Status.b)
; disable the gadget
DisableGadget(Gadget, Status)
; store status with gadget
SetGadgetData(Gadget, Status)
EndProcedure
Procedure.i SelectGadget(Gadget.i, StartGadget.i, EndGadget.i, Direction.i)
Protected Gadgets.i = EndGadget - StartGadget + 1, n.i = 0
If Gadget >= StartGadget And Gadget <= EndGadget
; skip disabled gadgets
Repeat
n + 1
Gadget + Direction
; keep within range
If Gadget < StartGadget
Gadget = EndGadget
ElseIf Gadget > EndGadget
Gadget = StartGadget
EndIf
Until (GetGadgetData(Gadget) = #Gadget_Enabled) Or (n >= Gadgets)
EndIf
ProcedureReturn Gadget
EndProcedure
Procedure AddWindowKeys(Window.i)
AddKeyboardShortcut(Window, #PB_Shortcut_Return, #Window_Key_Return)
AddKeyboardShortcut(Window, #PB_Shortcut_Escape, #Window_Key_Escape)
AddKeyboardShortcut(Window, #PB_Shortcut_Left, #Window_Key_Left)
AddKeyboardShortcut(Window, #PB_Shortcut_Right, #Window_Key_Right)
Key_Return_Off = #False
Key_Escape_Off = #False
Key_Left_Off = #False
Key_Right_Off = #False
EndProcedure
Procedure RemoveWindowKeys(Window.i)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Return)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Escape)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Left)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Right)
Key_Return_Off = #True
Key_Escape_Off = #True
Key_Left_Off = #True
Key_Right_Off = #True
EndProcedure
Procedure PollWindowKeys(Window.i, Gadget.i)
; You may want to add Gadgets not listed here in order to release shortcutkeys to them.
If Gadget >= 0
Select GadgetType(Gadget)
Case #PB_GadgetType_Editor, #PB_GadgetType_Web
RemoveKeyboardShortcut(Window, #PB_Shortcut_Return)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Left)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Right)
Key_Return_Off = #True
Key_Left_Off = #True
Key_Right_Off = #True
Case #PB_GadgetType_String, #PB_GadgetType_IPAddress, #PB_GadgetType_Calendar, #PB_GadgetType_ComboBox
RemoveKeyboardShortcut(Window, #PB_Shortcut_Left)
RemoveKeyboardShortcut(Window, #PB_Shortcut_Right)
Key_Left_Off = #True
Key_Right_Off = #True
Default
If Key_Return_Off
AddKeyboardShortcut(Window, #PB_Shortcut_Return, #Window_Key_Return)
Key_Return_Off = #False
EndIf
If Key_Left_Off
AddKeyboardShortcut(Window, #PB_Shortcut_Left, #Window_Key_Left)
Key_Left_Off = #False
EndIf
If Key_Right_Off
AddKeyboardShortcut(Window, #PB_Shortcut_Right, #Window_Key_Right)
Key_Right_Off = #False
EndIf
EndSelect
EndIf
EndProcedure
;}
;{ General stuff (I put this in App.pb, my input source file)
#App_Text_Cancel = "Cancel"
#App_Text_OK = "OK"
#App_Info = "Click 'Notation' and try using the Enter-key, Escape key, and the Arrow left/right on the radio's and buttons. (Does not work in this window)."
Enumeration
#App_Window_Main
#App_Window_Notation
EndEnumeration
Enumeration
#App_Label_Info
#App_Button_Notation
EndEnumeration
;}
;{ Notation stuff
Structure Notation_SettingsType
CurrencySign.s{1}
DecimalSign.s{1}
DateFormat.i
DateSeparator.s{1}
DateTemplate.s{13}
H12.b
TimeSeparator.s{1}
TimeTemplate.s{10}
EndStructure
;- Gadget Constants
Enumeration #PB_Compiler_EnumerationValue
#Notation_Frame3D_General
#Notation_Text_CurrencySign
#Notation_String_CurrencySign
#Notation_Text_DecimalSign
#Notation_Combo_DecimalSign
#Notation_Frame3D_Date
#Notation_Text_DateStyle
#Notation_Radio_DMY
#Notation_Radio_MDY
#Notation_Radio_YMD
#Notation_Text_DateSeparator
#Notation_Combo_DateSeparator
#Notation_Frame3D_Time
#Notation_Text_TimeDisplay
#Notation_Radio_12Hour
#Notation_Radio_24Hour
#Notation_Text_TimeSeparator
#Notation_Combo_TimeSeparator
#Notation_Button_OK
#Notation_Button_Cancel
EndEnumeration
;- Format Constants
Enumeration
#Notation_DMY
#Notation_MDY
#Notation_YMD
EndEnumeration
;- Globals
Global Notation_TSettings.Notation_SettingsType
;- Constants
#Notation_Title = "Notation"
#Notation_Days = "dd"
#Notation_Months = "mm"
#Notation_Years = "yyyy"
#Notation_Hours = "hh"
#Notation_Minutes = "mm"
#Notation_Seconds = "ss"
#Notation_Obr = "("
#Notation_Cbr = ")"
#Notation_Point = "Dot '.'"
#Notation_Comma = "Comma ','"
#Notation_General = "General"
#Notation_CurrencySign = "Currency symbol:"
#Notation_DecimalSign = "Decimal sign:"
#Notation_DecimalSigns = ".,"
#Notation_Date = "Date"
#Notation_Style = "Style:"
#Notation_DateFormat1 = "DMY"
#Notation_DateFormat2 = "MDY"
#Notation_DateFormat3 = "YMD"
#Notation_DateSeparator1 = "Dash '-'"
#Notation_DateSeparator2 = "Slash '/'"
#Notation_DateSeparator3 = "Dot '.'"
#Notation_DateSeparator4 = "Space"
#Notation_DateSeparators = "-/. "
#Notation_Display = "Display:"
#Notation_Separator = "Separator:"
#Notation_Time = "Time"
#Notation_12Hours = "12 hours"
#Notation_24Hours = "24 hours"
#Notation_TimeSeparator1 = "Colon ':'"
#Notation_TimeSeparator2 = "Dot '.'"
#Notation_TimeSeparators = ":."
#Notation_CurrencySign_Size = 1
;- Globals
Global Notation_OK.b
Global Notation_TDisplay.Notation_SettingsType
;- General Notation procedures
Procedure Notation_Templates(*TSettings.Notation_SettingsType)
With *TSettings
; date template
Select \DateFormat
Case #Notation_DMY
\DateTemplate = #Notation_Days + \DateSeparator + #Notation_Months + \DateSeparator + #Notation_Years
Case #Notation_MDY
\DateTemplate = #Notation_Months + \DateSeparator + #Notation_Days + \DateSeparator + #Notation_Years
Case #Notation_YMD
\DateTemplate = #Notation_Years + \DateSeparator + #Notation_Months + \DateSeparator + #Notation_Days
EndSelect
\DateTemplate = #Notation_Obr + \DateTemplate + #Notation_Cbr
; time template
\TimeTemplate = #Notation_Obr + #Notation_Hours + \TimeSeparator + #Notation_Minutes + \TimeSeparator + #Notation_Seconds + #Notation_Cbr
EndWith
EndProcedure
Procedure Notation_Default(*TSettings.Notation_SettingsType)
With *TSettings
\CurrencySign = "€"
\DecimalSign = Chr(44)
\DateFormat = #Notation_DMY
\DateSeparator = Chr(45)
\H12 = #False
\TimeSeparator = Chr(58)
EndWith
Notation_Templates(*TSettings)
EndProcedure
; Notation Window procedures
Procedure.b Notation_Button_OK()
Protected Text.s
With Notation_TDisplay
; currency sign
\CurrencySign = GetGadgetText(#Notation_String_CurrencySign)
; decimal sign
If GetGadgetState(#Notation_Combo_DecimalSign) = 0
; point
\DecimalSign = Chr(46)
Else
; comma
\DecimalSign = Chr(44)
EndIf
; date format
If GetGadgetState(#Notation_Radio_DMY)
\DateFormat = #Notation_DMY
ElseIf GetGadgetState(#Notation_Radio_MDY)
\DateFormat = #Notation_MDY
Else
\DateFormat = #Notation_YMD
EndIf
; date separator
Select GetGadgetState(#Notation_Combo_DateSeparator)
Case 0 ;dash '-'
\DateSeparator = Chr(45)
Case 1 ;slash '/'
\DateSeparator = Chr(47)
Case 2 ;point '.'
\DateSeparator = Chr(46)
Default
\DateSeparator = Chr(32)
EndSelect
; clock
If GetGadgetState(#Notation_Radio_12Hour)
\H12 = #True
Else
\H12 = #False
EndIf
; time separator
Select GetGadgetState(#Notation_Combo_TimeSeparator)
Case 0
; colon
\TimeSeparator = Chr(58)
Case 1
; dot
\TimeSeparator = Chr(46)
EndSelect
EndWith
Notation_Templates(@Notation_TDisplay)
Notation_OK = #True
ProcedureReturn #True
EndProcedure
Procedure.b Notation_Window_Open(Window.i, ParentWindow.i)
Protected ParentWindowID.i = WindowID(ParentWindow)
If OpenWindow(Window, 233, 57, 280, 340, #Notation_Title, #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_WindowCentered | #PB_Window_Invisible, ParentWindowID)
;StickyWindow(Window, #True)
With Notation_TDisplay
Frame3DGadget(#Notation_Frame3D_General, 10, 10, 260, 90, #Notation_General)
TextGadget(#Notation_Text_CurrencySign, 30, 33, 90, 20, #Notation_CurrencySign)
StringGadget(#Notation_String_CurrencySign, 130, 30, 40, 20, "")
TextGadget(#Notation_Text_DecimalSign, 30, 63, 90, 20, #Notation_DecimalSign)
ComboBoxGadget(#Notation_Combo_DecimalSign, 130, 60, 120, 20)
Frame3DGadget(#Notation_Frame3D_Date, 10, 110, 260, 90, #Notation_Date)
TextGadget(#Notation_Text_DateStyle, 30, 133, 50, 20, #Notation_Style)
OptionGadget(#Notation_Radio_DMY, 90, 130, 50, 20, #Notation_DateFormat1)
OptionGadget(#Notation_Radio_MDY, 150, 130, 50, 20, #Notation_DateFormat2)
OptionGadget(#Notation_Radio_YMD, 210, 130, 50, 20, #Notation_DateFormat3)
TextGadget(#Notation_Text_DateSeparator, 30, 163, 90, 20, #Notation_Separator)
ComboBoxGadget(#Notation_Combo_DateSeparator, 130, 160, 120, 20)
Frame3DGadget(#Notation_Frame3D_Time, 10, 210, 260, 90, #Notation_Time)
TextGadget(#Notation_Text_TimeDisplay, 30, 230, 80, 20, #Notation_Display)
OptionGadget(#Notation_Radio_12Hour, 130, 230, 60, 20, #Notation_12Hours)
OptionGadget(#Notation_Radio_24Hour, 200, 230, 60, 20, #Notation_24Hours)
TextGadget(#Notation_Text_TimeSeparator, 30, 263, 90, 20, #Notation_Separator)
ComboBoxGadget(#Notation_Combo_TimeSeparator, 130, 260, 120, 20)
ButtonGadget(#Notation_Button_OK, 100, 310, 80, 22, #App_Text_OK)
ButtonGadget(#Notation_Button_Cancel, 190, 310, 80, 22, #App_Text_Cancel)
EndWith
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure Notation_Window_Load(Window.i)
Protected Text.s, Value.i = 0
; define keys
AddWindowKeys(Window)
; load
With Notation_TDisplay
; currency sign
SetGadgetText(#Notation_String_CurrencySign, \CurrencySign)
; decimal sign
AddGadgetItem(#Notation_Combo_DecimalSign, -1, #Notation_Point)
AddGadgetItem(#Notation_Combo_DecimalSign, -1, #Notation_Comma)
If \DecimalSign = Chr(44) ;comma
Value = 1
EndIf
SetGadgetState(#Notation_Combo_DecimalSign, Value)
; date format
If \DateFormat = #Notation_DMY
Value = #Notation_Radio_DMY
ElseIf \DateFormat = #Notation_MDY
Value = #Notation_Radio_MDY
Else
Value = #Notation_Radio_YMD
EndIf
SetGadgetState(Value, 1)
; date separator
AddGadgetItem(#Notation_Combo_DateSeparator, -1, #Notation_DateSeparator1)
AddGadgetItem(#Notation_Combo_DateSeparator, -1, #Notation_DateSeparator2)
AddGadgetItem(#Notation_Combo_DateSeparator, -1, #Notation_DateSeparator3)
AddGadgetItem(#Notation_Combo_DateSeparator, -1, #Notation_DateSeparator4)
Select \DateSeparator
Case Chr(45) ;dash '-'
Value = 0
Case Chr(47) ;slash '/'
Value = 1
Case Chr(46) ;dot '.'
Value = 2
Default ;space
Value = 3
EndSelect
SetGadgetState(#Notation_Combo_DateSeparator, Value)
; clock
If \H12 = #True
SetGadgetState(#Notation_Radio_12Hour, 1)
Else
SetGadgetState(#Notation_Radio_24Hour, 1)
EndIf
; time separator
AddGadgetItem(#Notation_Combo_TimeSeparator, -1, #Notation_TimeSeparator1)
AddGadgetItem(#Notation_Combo_TimeSeparator, -1, #Notation_TimeSeparator2)
If \TimeSeparator = Chr(58)
; colon
Value = 0
Else
; dot
Value = 1
EndIf
SetGadgetState(#Notation_Combo_TimeSeparator, Value)
EndWith
HideWindow(Window, #False)
EndProcedure
Procedure Notation_Window_Unload(Window.i)
RemoveWindowKeys(Window)
EndProcedure
Procedure.b Notation_EventGadget(Gadget.i, Event.i)
; You can also test Event for #PB_EventType_LeftDoubleClick etc.
; This is useful if you have a ListView or ListIcon gadget.
Select Gadget
Case #Notation_Button_OK
If Notation_Button_OK()
ProcedureReturn #True
EndIf
Case #Notation_Button_Cancel
ProcedureReturn #True
Default
If Event = #PB_Shortcut_Return
If Notation_Button_OK()
ProcedureReturn #True
EndIf
ElseIf Gadget = #Notation_String_CurrencySign
LimitStringGadget(Gadget, #Notation_CurrencySign_Size)
EndIf
EndSelect
EndProcedure
Procedure Notation_Events(Window.i)
Protected ActiveGadget.i, Event.i, Direction.i, Quit.b = #False
ActiveGadget = #Notation_String_CurrencySign
Repeat
SetActiveGadget(ActiveGadget)
PollWindowKeys(Window, ActiveGadget)
Event = WaitWindowEvent()
ActiveGadget = GetActiveGadget()
Select Event
Case #PB_Event_Gadget
Quit = Notation_EventGadget(EventGadget(), EventType())
Case #PB_Event_Menu
Event = EventMenu()
Select Event
Case #Window_Key_Return
Quit = Notation_EventGadget(ActiveGadget, #PB_Shortcut_Return)
Case #Window_Key_Escape
Quit = #True
Case #Window_Key_Left, #Window_Key_Right
If Event = #Window_Key_Left
Direction = -1
Else
Direction = 1
EndIf
Select ActiveGadget
Case #Notation_Radio_12Hour To #Notation_Radio_24Hour
ActiveGadget = SelectGadget(ActiveGadget, #Notation_Radio_12Hour, #Notation_Radio_24Hour, Direction)
Case #Notation_Radio_DMY To #Notation_Radio_YMD
ActiveGadget = SelectGadget(ActiveGadget, #Notation_Radio_DMY, #Notation_Radio_YMD, Direction)
Default
ActiveGadget = SelectGadget(ActiveGadget, #Notation_Button_OK, #Notation_Button_Cancel, Direction)
EndSelect
EndSelect
Case #PB_Event_CloseWindow
Quit = #True
EndSelect
Until Quit
EndProcedure
Procedure Notation_Load()
CopyStructure(Notation_TSettings, Notation_TDisplay, Notation_SettingsType)
Notation_OK = #False
EndProcedure
Procedure Notation_UnLoad()
If Notation_OK
CopyStructure(Notation_TDisplay, Notation_TSettings, Notation_SettingsType)
EndIf
; clean up
ClearStructure(Notation_TDisplay, Notation_SettingsType)
EndProcedure
Procedure.b Notation_Open(Window.i, ParentWindow.i)
Notation_Load()
If Notation_Window_Open(Window, ParentWindow)
Notation_Window_Load(Window)
Notation_Events(Window)
Notation_Window_Unload(Window)
CloseWindow(Window)
Notation_UnLoad()
ProcedureReturn #True
EndIf
Notation_UnLoad()
ProcedureReturn #False
EndProcedure
;}
Procedure.b App_Open()
Protected Event.i
If OpenWindow(#App_Window_Main, 0, 0, 400, 200, "SDI-example with Window key-navigation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget(#App_Label_Info, 20, 20, 360, 120, #App_Info)
ButtonGadget (#App_Button_Notation, 290, 160, 80, 22, "Notation")
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
If EventGadget() = #App_Button_Notation
; get default (European) notation settings
DisableWindow(#App_Window_Main, #True)
Notation_Default(@Notation_TSettings)
If Not Notation_Open(#App_Window_Notation, #App_Window_Main)
MessageRequester("Notation", "Error opening window.", #MB_ICONERROR)
EndIf
DisableWindow(#App_Window_Main, #False)
SetActiveWindow(#App_Window_Main)
EndIf
EndIf
Until Event = #PB_Event_CloseWindow
ProcedureReturn #True
EndIf
EndProcedure
If Not App_Open()
MessageRequester("Notation", "Programm could not start.", #MB_ICONERROR)
EndIf
End