"Gadget(s)" für Zeiteingabe gesucht

Für allgemeine Fragen zur Programmierung mit PureBasic.
Omi
Beiträge: 143
Registriert: 25.03.2013 09:59

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Omi »

Hallo Shardik,

Off-topic: Bin später dran als ich wollte: Als alter Fan hab grad aus Neugier bei 3Sat-Yes live reingehört, erwartete ein musikalisches Elend und blieb hängen. Sagenhaft wie die alten Herren aufspielen inkl. einwandfreiem Sound - hab ehrlich noch feuchte Augen.

Das schaut ja schon mal hervorragend für den Mac aus :praise: - und man müsste Events, Selektionen, Verwaltung etc. nicht Gtk-like zu irgendwas halbwegs funktionierendem zusammenzimmern. <)

Scheinbar gab es bzgl. TimePicker in frühen Gtk's mal was - das Motto 'was könnt' ma noch alles 'deprecaten'' hat's wohl wieder dahingerafft ;-).
Für Qt gibt's ein QTimeEdit. Schau'n mer mal ob die PB-Zukunft (und meine oder anderer Leute Zeit) hier was möglich macht. Ich bin z.Zt. bei Qt noch auf Stand #Null.

Bin gerade beim Rummachen mit etwaigen Flags. Ich denke, man sollte OS-spezifische 'Eigenheiten' nicht komplette unterdrücken, auch wenn die Bedienung und Elemente dann nicht ganz 1:1 sind. (Das PB-SpinGadget z.B. unterscheidet sich ja auch je OS und es ist ja nicht für den Programmierer sondern für Anwender gedacht - und der sitzt auf SEINEM System und hat was anderes im Sinn als die Anwendung OS-abhängig zu vergleichen.)

Beim Windows-Teil hätte ich die 12h-Variante mit anhängendem AM/PM ermöglicht und würde lt. Wine-Test auch funktionieren.
Bei Linux würde ich's höchstens mal nachrüsten, falls mir eine elegante, codesparende Idee dazu kommt. Als Ausgleich dafür (und für (noch?) fehlende Pfeile) hat die Bedienung Vollausstattung.
Für die Mac-Variante könnte man einen 12h-Modus auch ermöglichen (+ ebenfalls Pfeile aus/ein) falls möglich.
Ein read-only-Modus für alle wäre vielleicht noch wünschenswert.

Schön wär's abschließend als Modul - aber da bin ich komplette ungeübt!

Dann rutscht's mal alle gut in's neue Jahr
Charly
PureBasic Linux-API-Library: http://www.chabba.de
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von ccode_new »

Hi!
Für Qt gibt's ein QTimeEdit.
Jepp!

Aber für eine C-Einbindung müsste man das ja erst kompliziert portieren, oder ?

GTK+ ist unter Linux-Standard und man kann es sehr einfach aus PureBasic heraus nutzen.
Und je besser man sich mit dieser API auskennt umso leichter kann man diese auch direkt mit C nutzen.
Was auch schnell mal kleine native GUI-Programme mit GTK+ für Arm-Prozessoren (Raspi) ermöglicht.

Ich bin der Meinung man sollte die jeweils beste API unter dem jeweiligen Betriebssystem nutzen.
Und das ist eigentlich die Mitgelieferte.
Man kann natürlich auch immer eine ganze Runtime aus Bibliotheken und Interpretern
für jedes Betriebssystem mitschleifen, aber das finde zumindest ich nicht so toll.

Man kann sich aber auch gut eine QT-GUI mit dem QTimeEdit in Python basteln und direkt aus PureBasic kontrollieren.
Python unterstützt nämlich QT sehr gut und es hat eine gute C-Bindung.
"nuitka" ist im Übrigen auch ein schöner Python- C Übersetzer ;)
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Shardik
Beiträge: 738
Registriert: 25.01.2005 12:19

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Shardik »

Leider wird es für den Mac doch etwas komplizierter. Da das NSDatePicker-Control ja kein natives PureBasic-Gadget ist, muss ein Callback erstellt werden, der aufgerufen wird, wenn ein Segmentwert geändert wird. Der folgende Beispiel-Code setzt dies um und liest bei jeder Änderung den TimePicker aus:

Code: Alles auswählen

EnableExplicit

#Event_TimePickerChanged = #PB_Event_FirstCustomValue
#NSDatePickerStyleTextField = 2
#NSDatePickerStyleTextFieldAndStepper = 0
#NSHourMinuteSecondDatePickerElementFlag = 14

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define *CurrentDate
Define CurrentDate.S
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define Selector.I = 0 +
  sel_registerName_("datePickerCell:validateProposedDateValue:timeInterval:")
Define StartDate.S
Define *StartDate
Define TimePicker.I

ProcedureC TimePickerChangeCallback(Object.I, Selector.I, Cell.I,
  ProposedValue.I, ProposedTimeInterval.I)
  PostEvent(#Event_TimePickerChanged)
EndProcedure

Procedure CreateTimePicker(WindowID.I, x.I, y.I, Width.I, Height.I, Style.I,
  StartDate.S)
  Protected Frame.NSRect
  Protected *StartDate
  Protected TimePicker.I

  Frame\origin\x = x
  Frame\origin\y = y
  Frame\size\width = Width
  Frame\size\height = Height
  TimePicker = CocoaMessage(0, CocoaMessage(0, 0, "NSDatePicker alloc"),
    "initWithFrame:@", @Frame)
  CocoaMessage(0, TimePicker,
    "setDatePickerStyle:", Style)
  CocoaMessage(0, TimePicker,
    "setDatePickerElements:", #NSHourMinuteSecondDatePickerElementFlag)
  CocoaMessage(0, CocoaMessage(0, WindowID(WindowID), "contentView"),
    "addSubview:", TimePicker)
  *StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @StartDate)
  CocoaMessage(0, TimePicker, "setDateValue:@", @*StartDate)

  ProcedureReturn TimePicker
EndProcedure

StartDate = FormatDate("%yyyy-%mm-%dd", Date()) + " 00:00:00 +0100"
OpenWindow(0, 270, 100, 160, 90, "TimePicker")
CreateStatusBar(0, WindowID(0))
AddStatusBarField(#PB_Ignore)
StatusBarText(0, 0, "00:00:00", #PB_StatusBar_Center)
TimePicker = CreateTimePicker(0, 40, 25, 80, 25,
  #NSDatePickerStyleTextFieldAndStepper, StartDate)

; ----- Setup callback for changes in TimePicker
class_addMethod_(DelegateClass, Selector, @TimePickerChangeCallback(),
  "v@:@@@")
CocoaMessage(0, TimePicker, "setDelegate:", AppDelegate)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #Event_TimePickerChanged
      *CurrentDate = CocoaMessage(0, TimePicker, "dateValue")
      CurrentDate = PeekS(CocoaMessage(0,
        CocoaMessage(0, *CurrentDate, "description"), "UTF8String"),
        -1, #PB_UTF8)
      StatusBarText(0, 0, Mid(CurrentDate, 11, 9), #PB_StatusBar_Center)
  EndSelect
ForEver
Bild
Benutzeravatar
Shardik
Beiträge: 738
Registriert: 25.01.2005 12:19

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Shardik »

So, ich habe jetzt ein erstes Multiplattform-Modul erstellt für MacOS und Windows. Der viel komplexere Teil für Linux muss noch eingebaut werden. Charly hat ja in diesem Thread schon eindrucksvoll gezeigt, wie man einen Windows-ähnlichen TimePicker in Linux realisieren kann.

Code: Alles auswählen

EnableExplicit

Define StartDate.S
Define TimePicker.I

DeclareModule TimePicker
  #Event_TimePickerChanged = #PB_Event_FirstCustomValue

  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
    CompilerCase #PB_OS_MacOS
      #NSDatePickerStyleTextFieldAndStepper = 0
      #NSHourMinuteSecondDatePickerElementFlag = 14
    CompilerCase #PB_OS_Windows
  CompilerEndSelect

  Declare CreateTimePicker(WindowID.I, x.I, y.I, Width.I, Height.I,
    StartDate.S)
  Declare.S GetTime(TimePicker.I)
EndDeclareModule

Module TimePicker
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
    CompilerCase #PB_OS_MacOS
      ProcedureC TimePickerChangedCallback(Object.I, Selector.I, Cell.I,
        ProposedValue.I, ProposedTimeInterval.I)
        PostEvent(#Event_TimePickerChanged)
      EndProcedure

      Procedure InitCallback(TimePicker.I)
        Protected AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
          "NSApplication sharedApplication"), "delegate")
        Protected DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
        Protected Selector.I = sel_registerName_("datePickerCell:" +
          "validateProposedDateValue:timeInterval:")

        class_addMethod_(DelegateClass, Selector, @TimePickerChangedCallback(),
          "v@:@@@")
        CocoaMessage(0, TimePicker, "setDelegate:", AppDelegate)
      EndProcedure
      
      Procedure CreateTimePicker(WindowID.I, x.I, y.I, Width.I, Height.I,
        StartDate.S)
        Protected Frame.NSRect
        Protected *StartDate
        Protected TimePicker.I
        
        Frame\origin\x = x
        Frame\origin\y = y
        Frame\size\width = Width
        Frame\size\height = Height
        TimePicker = CocoaMessage(0, CocoaMessage(0, 0, "NSDatePicker alloc"),
          "initWithFrame:@", @Frame)
        CocoaMessage(0, TimePicker,
          "setDatePickerStyle:", #NSDatePickerStyleTextFieldAndStepper)
        CocoaMessage(0, TimePicker,
          "setDatePickerElements:", #NSHourMinuteSecondDatePickerElementFlag)
        CocoaMessage(0, CocoaMessage(0, WindowID(WindowID), "contentView"),
          "addSubview:", TimePicker)
        StartDate + " +0100"
        *StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @StartDate)
        CocoaMessage(0, TimePicker, "setDateValue:@", @*StartDate)
        InitCallback(TimePicker)

        ProcedureReturn TimePicker
      EndProcedure

      Procedure.S GetTime(TimePicker.I)
        Protected *CurrentDate
        Protected CurrentDate.S

        *CurrentDate = CocoaMessage(0, TimePicker, "dateValue")
        CurrentDate = PeekS(CocoaMessage(0,
          CocoaMessage(0, *CurrentDate, "description"), "UTF8String"),
          -1, #PB_UTF8)
        ProcedureReturn Mid(CurrentDate, 11, 9)
      EndProcedure
    CompilerCase #PB_OS_Windows
      Procedure WindowCallback(WindowHandle.I, Msg.I, WParam.I, LParam.I)
        Shared TimePicker.I
        Shared TimePickerWindow.I

        Protected *NMHdr.NMHDR

        If Msg = #WM_NOTIFY
          If WindowHandle = WindowID(TimePickerWindow)
            *NMHdr = LParam

            If IsGadget(TimePicker)
              If *NMHdr\hwndFrom = GadgetID(TimePicker)
                If *NMHdr\code = #DTN_DATETIMECHANGE
                  PostEvent(#Event_TimePickerChanged)
                EndIf
              EndIf
            EndIf
          EndIf
        EndIf

        ProcedureReturn #PB_ProcessPureBasicEvents
      EndProcedure

      Procedure CreateTimePicker(WindowID.I, x.I, y.I, Width.I, Height.I,
        StartDate.S)
        Shared TimePicker.I

        InitCommonControls_()
        TimePicker = DateGadget(#PB_Any, x, y, Width, Height, "",
          ParseDate("%yyyy-%mm-%dd %hh:%ii:%ss", StartDate), #DTS_TIMEFORMAT)
        TimePickerWindow = WindowID
        SetWindowCallback(@WindowCallback(), TimePickerWindow)

        ProcedureReturn TimePicker
      EndProcedure

      Procedure.S GetTime(TimePicker.I)
        Protected CurrentTime.S
        Protected SystemTime.SYSTEMTIME
        
        SendMessage_(GadgetID(TimePicker), #MCM_GETCURSEL, 0, @SystemTime)
        CurrentTime = RSet(Str(SystemTime\wHour), 2, "0") + ":" +
          RSet(Str(SystemTime\wMinute), 2, "0") + ":" +
          RSet(Str(SystemTime\wSecond), 2, "0")
        
        ProcedureReturn CurrentTime
      EndProcedure
  CompilerEndSelect
EndModule

StartDate = FormatDate("%yyyy-%mm-%dd", Date()) + " 00:00:00"
OpenWindow(0, 270, 100, 160, 90, "TimePicker")
CreateStatusBar(0, WindowID(0))
AddStatusBarField(#PB_Ignore)
StatusBarText(0, 0, "00:00:00", #PB_StatusBar_Center)
TimePicker = TimePicker::CreateTimePicker(0, 40, 25, 80, 25, StartDate)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case TimePicker::#Event_TimePickerChanged
      StatusBarText(0, 0, TimePicker::GetTime(TimePicker),
        #PB_StatusBar_Center)
  EndSelect
ForEver
Ich habe dieses Modul erfolgreich mit PB 5.46 x86 unter diesen Betriebssystemen getestet:
- MacOS 10.6.8 (Snow Leopard)
- Windows 7 SP1 x64
- Windows 10 x64

Bild
Omi
Beiträge: 143
Registriert: 25.03.2013 09:59

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Omi »

Herzlichen Dank shardik,

Ich hätte mittlerweile die Windows und Linux Version auch zusammengesetzt und beide mit ähnlicher PB-Gadget-Syntax, ein paar Flags dazu und die Zugriffe erstellt. Als einziges müßte vom Programmierer z.Zt. jeweils noch der Callback für Windows auf die Gadget-Nummern angepasst werden, damit das PostEvent auch an die angelegten Gadgetnummern sendet.

Es stellt sich auch heraus, dass das StringGadget per #PB_Any nicht den Zeiger auf das GtkEntry zurückliefert ? (zumindest auf meinem Linux schepperte es mit #PB_Any).
Deshalb ist es nötig sowohl mit einer definierter Gadget-Nummer (#Gadget) zu arbeiten als auch mit dem Pointer auf das API-Element (ala GadgetID()).

Ich wollte Dir gestern noch eine PM senden, ob ich Deine 1. Mac-Version mal selbst einarbeiten und Dir zum 'lauffähig machen' senden soll oder Du es selbst übernehmen möchtest.
Leider hat sich gestern Abend das Handwerk angekündigt, die heute die neuen Zimmertüren endlich einbauen möchten. Wartezeit? Möchte ich gar nicht laut sagen >_< .

Ich werde jetzt am besten mal versuchen, mein Erreichtes in Dein Modul zu bringen, und Du evtl. den Mac-Teil noch anpasst (Gadget-ähnliche Parameter-Reihenfolge + Get-Set-Zugriffe + Flags). Ich sende es Dir dann evtl. mal vorab als PM, falls möglich, damit der Thread nicht zu viel Halbfertiges abbekommt.

Bis dahin, erneut vielen Dank für Deine :praise: Mühe
Charly
PureBasic Linux-API-Library: http://www.chabba.de
Benutzeravatar
mk-soft
Beiträge: 3700
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von mk-soft »

Es stellt sich auch heraus, dass das StringGadget per #PB_Any nicht den Zeiger auf das GtkEntry zurückliefert ? (zumindest auf meinem Linux schepperte es mit #PB_Any).
Deshalb ist es nötig sowohl mit einer definierter Gadget-Nummer (#Gadget) zu arbeiten als auch mit dem Pointer auf das API-Element (ala GadgetID()).
Ein Gedankenfehler?

Code: Alles auswählen

GadgetPB = StringGadget(#PB_Any, 10, 10, 200, 25, "")
Widget = GadgetID(GadgetPB)
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Shardik
Beiträge: 738
Registriert: 25.01.2005 12:19

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Shardik »

Omi hat geschrieben:Es stellt sich auch heraus, dass das StringGadget per #PB_Any nicht den Zeiger auf das GtkEntry zurückliefert ? (zumindest auf meinem Linux schepperte es mit #PB_Any).
Ich kann das Problem leider auch nicht reproduzieren. Ich lasse mit dem folgenden Programm die Hierarchie der Widgets im Fenster anzeigen und egal ob ich das StringGadget mit z.B. der Ziffer 0 anlege oder mit #PB_Any: auf meinem Linux Mint 18.3 x64 Cinnamon mit PB 5.62 x64 oder PB 5.70 x64 wird immer korrekt "GtkEntry" als Widget-Name für GadgetID() ausgegeben:

Bild

Code: Alles auswählen

EnableExplicit

ImportC ""
  g_type_check_instance_is_a(*Instance.GTypeInstance, *Type.GTypeClass)
EndImport

Define StringGadgetID.I
Define SubnodeLevel.I
Define WidgetTree.I
Define GTKVersion.I

NewList FallbackSubnodeLevel.I()

Procedure.I GetChildren(*Widget.GtkWidget, WidgetTree.I)
  Shared FallbackSubnodeLevel.I()
  Shared SubnodeLevel.I

  Protected *Child.GtkWidget
  Protected ChildrenCount.I
  Protected *ChildrenList.GList
  Protected i.I
  Protected *WidgetName
 
  If g_type_check_instance_is_a(*Widget\object\parent_instance\g_type_instance,
    gtk_container_get_type_()) = #False
    If ListSize(FallbackSubnodeLevel()) > 0
      LastElement(FallbackSubnodeLevel())
      SubnodeLevel = FallbackSubnodeLevel()
      DeleteElement(FallbackSubnodeLevel())
    EndIf
  Else
    *ChildrenList = gtk_container_get_children_(*Widget)
    ChildrenCount = g_list_length_(*ChildrenList)
   
    If ChildrenCount > 0
      If ChildrenCount > 1
        AddElement(FallbackSubnodeLevel())
        FallbackSubnodeLevel() = SubnodeLevel
      EndIf

      For i = 0 To ChildrenCount - 1
        *Child = g_list_nth_data_(*ChildrenList, i)
        *WidgetName = gtk_widget_get_name_(*Child)
        AddGadgetItem(WidgetTree, -1, PeekS(*WidgetName, -1, #PB_UTF8), 0, SubnodeLevel)
        SubnodeLevel + 1
        GetChildren(*Child, WidgetTree)
      Next i

      g_list_free_(*ChildrenList)
    EndIf
  EndIf
EndProcedure

Procedure ExamineWindow(WindowID.I, WidgetTree.I)
  Shared FallbackSubnodeLevel.I()
  Shared SubnodeLevel.I

  ClearGadgetItems(WidgetTree)
  AddGadgetItem(WidgetTree, -1, "GtkWindow", 0, 0)
  ClearList(FallbackSubnodeLevel())
  SubnodeLevel = 1
  GetChildren(WindowID(WindowID), WidgetTree)
  gtk_tree_view_expand_all_(GadgetID(WidgetTree))
EndProcedure

If Subsystem("gtk2")
  GTKVersion = 2
Else
  GTKVersion = 3
EndIf

OpenWindow(0, 100, 100, 350, 250, "Widget map of StringGadget in GTK " +
  GTKVersion)
StringGadgetID = StringGadget(#PB_Any, 10, 10, 90, 24, "StringGadget")
WidgetTree = TreeGadget(#PB_Any, GadgetWidth(StringGadgetID) + 20, 10,
  WindowWidth(0) - GadgetWidth(StringGadgetID) - 30, WindowHeight(0) - 20)
ExamineWindow(0, WidgetTree)
MessageRequester("Info", "Widget-Name von StringGadget: " +
  PeekS(gtk_widget_get_name_(GadgetID(StringGadgetID)), -1, #PB_UTF8)) 

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Omi
Beiträge: 143
Registriert: 25.03.2013 09:59

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Omi »

Danke für die Rückmeldung.

Zum 'falschen Zeiger' zu den Objekten.
Da es alle machen, war ich mir auch relativ sicher, es so umsetzen zu können. Auf Xubuntu 18.04/x64 + PB5.70B3 war's eindeutig eine falsche Adresse im Rückgabewert mit (KORREKTUR:) einer Menge Gtk3-Warnungen und Versagen der Zugriffe (die Adresse auch verdächtig 'niedrig' + gtk_widget_get_name(*widget) gibt '0' zurück!) per #PB_Any. Ab '0' fürs Gadget war's okay!

"Any"-way :wink: Wenn's mal fuchst wählt man den sichereren Weg und hab's jetzt mal mit #Gadget + sowieso notwendiger GadgetID (Zeiger) am laufen. (Nacharbeit ist ja jederzeit möglich falls ich das Problem lokalisieren und umschiffen kann!) Ich probier's auch nochmal durch die Systeme durch.

Allerdings wüßte ich auch von keiner 'dokumentierten Eigenschaft', dass der Rückgabe-Wert bei Gadgeterstellung ein Pointer zum OS-Objekt sein muss.
In anderen Fällen hatte ich auch schon Pleiten: z.B. TaskBar-Rückgabewert bei Gtk2 falsch (oder nicht direkt adressiert), bei Gtk3 ist's okay., und vereinzelt weitere Fälle. 100% verlässlich war es noch nie.

@shardik
Da der Code mittlerweile 'lang' ist und im Mac-Teil teils nicht fertig und teils ungetestet + Blindflug-Arbeit hab ich Dir eine PM mit Link zum Code geschickt, um hier nicht alles zuzumüllen.

Gruß, Charly

PS:
Verdammt, verdammt, verdammt !!! :twisted: (Zitat aus: 'Auch der Sheriff braucht mal Hilfe'.)
Das hat man davon, wenn man IMMER #Gadget + GadgetID(#Gadget) verwendet.
mk-soft hat's ja deutlich geschrieben, INKLUSIVE ???
  • #Gadget >= 0 : 1. Rückgabe ist GadgetID (= Zeiger auf Objekt) -> GadgetID(GadgetID) = wäre natürlich Müll
    #Gadget >= 0 : 2. GadgetID(#Gadget) (= ebenfalls Zeiger auf Objekt)
    #Gadget = #PB_Any: Rückgabe entspricht #Gadget (= NICHT GadgetID) -> GadgetID = GadgetID(#Gadget aus #PB_Any Rückgabewert)
Nach einigen Jahren hat's der Bub auch begriffen >:)
PureBasic Linux-API-Library: http://www.chabba.de
Omi
Beiträge: 143
Registriert: 25.03.2013 09:59

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von Omi »

Sodele, das 'TimeGadget' liegt in einer PB-Gadget-ähnlichen, Multi-OS Form vor für Linux Gtk2/Gtk3, MacOS und Windows und heißt jetzt
  • TimePickerGadget
Bedienung, Möglichkeiten und Flags sind etwas OS-abhängig - dies ist aber beim PB-DateGadget nicht anders!

Shardik hat sich (trotz ausgefallenem Weihnachtsurlaub) mächtig reingehängt und den MacOS-Teil angepasst sowie das Ganze auch noch in Module-Form gebracht.

Vom Umfang geht es z.Zt. etwas in Richtung amerikanischer SUV (= Deutscher Gigaliner) ;-) - aber wir hamm's ja.

- Das 'TimePickerGadget' (hält namentlich PureBasic den Weg für ein 'TimeGadget' frei) wird ähnlich zu PB-Gadgets angelegt.
Details sind im Header des Sourcecodes zu ersehen!
- Die zusätzliche Angabe des Parameters #Window ist jedoch zwingend (und wie ich beim Tippen dieser Zeilen grad merke wird hier auch (noch) kein #PB_Any akzeptiert!).
- Der Parameter #Gadget kann eine PBNo/Konstante (> -1) oder #PB_Any sein. (#Gadget kann als Dummy zur Erstellung verwendet werden, wird aber später nicht mehr benötigt!)
- Für lesende oder schreibende Zugriffe auf das Gadget (s.u.) wird nicht die PB-GadgetNo (ala #Gadget) sondern der Zeiger/OS-Handle auf das OS-Objekt benötigt! (ala GadgetID), da im MacOS keinerlei Dummy-PB-Gadget dafür existiert!
- Lesen und Schreiben einer Uhrzeit (Tageszeit als String oder Sekunden seit der vergangenen Mitternacht) geschieht mit ...
  • GetTimePickerGadgetState(GadgetId)
    GetTimePickerGadgetText(GadgetId)
    SetTimePickerGadgetState(GadgetId, TimeVal)
    SetTimePickerGadgetText(GadgetId, sTimeString.s) als formatierter String im Format #TPG_TimeMask24h (= "%hh:%ii:%ss")
- Änderungen am TimePickerGadget lösen ein Event vom EventType: #PB_EventType_Change aus
- Ich hab noch einige OS-abhängigen Flags und Konstanten dafür integriert, siehe im Code-Header.
- Der Zugriff auf die Funktionen erfolgt (bei Verwendung von UseModule TimePickerGadget mit den Funktionsnamen,
ohne Verwendung von UseModule TimePickerGadget mit, den Funktionsaufrufen vorangestelltem TimePickerGadget::). Siehe auch in der folgenden Demo.

TimePickerGadget-Modul, All OS, eingebettet in einen Democode mit 2 Gadgets auf 2 Fenster verteilt: V0.21, 2018-01-20

Code: Alles auswählen

; Module/File:     TimePickerGadget_AllOS_V0_21.pb
; Function:        Gadget to edit, set and get time as module
; Author:          Teamwork: Omi + Shardik + others (netmaestro, PB, ..)
; Date:            Jan. 19, 2019
; Version:         0.21
; Target OS:       Windows, Linux Gtk2/Gtk3, MacOS
; Target Compiler: PureBasic 5.46/5.62/5.70
;                  Compilation on MacOS 10.6.8 (Snow Leopard) with PB 5.46/5.62 only possible with x86 compiler
;                  Last version supporting x64 compilation on Snow Leopard was PB 5.43
;                  Minimum MacOS version for 5.70 x86 + x64: 10.8 (Mountain Lion)
; Links to topic:  https://www.purebasic.fr/german/viewtopic.php?f=3&t=31214
;                  https://www.purebasic.fr/english/viewtopic.php?f=12&t=23797
;                  https://www.purebasic.fr/english/viewtopic.php?t=33184
;                  https://www.purebasic.fr/english/viewtopic.php?f=3&t=30025
;--------------------------------------------------------------

;V0.10 : Access with Gadget constant & GadgetNo (no Id)
;V0.11 : Access with var for GadgetId & GadgetId (no No/#Gadget)
;V0.12 : Trial to adapt and complete the Mac part
;V0.13 : 2 different module accesses in EventLoop with and without UseModule TimePickerGadget
;V0.14 : #PB_Any now works as #Gadget for TimePickerGadget
;V0.15 : Mac part bugs fixed:
;        - GetTimePickerGadgetState(): Time had to be extracted from sCurrentDate and
;          ProcedureReturn Mid(sCurrentDate, 11, 9)
;          had to be changed to
;          ProcedureReturn TimeVal
;        - TimePickerGadget(): Date and time offset had to be added to sTime
;        - SetTimePickerGadgetText(): Date and time offset had to be added to sTimeString
;        - SetTimePickerGadgetState(): Date and time offset had to be added to sTime and sTime
;        General Mac problems still to be solved:
;        - Cocoa class NSDate needs time offset to UTC: currently staticly set to
;          +01:00 for German timezone (Berlin)
;        - Callback integration still necessary to update the TextGadgets "Time set:" and "Time value set (=seconds):"
;V0.16 : MacOS callback implemented
;V0.17 : Added missing debug warnings on not available flags. Flag names corrected in warnings.
;V0.18 : MacOS Sierra bug fix: in GetTimePickerGadgetState(GadgetId) und GetTimePickerGadgetText(GadgetId) one hour
;        UTC correction had to be added (currently correct only in German timezone!), but now Snow Leopard display wrong time!
;V0.19 : For MacOS automatic detection of UTC offset implemented
;        Detection of MacOS version added to add UTC offset only in Get-Procedures for versions newer than Snow Leopard
;V0.20 : Now #PB_Any instead of GadgetNo for second window for demo part
;        Shardik: Fixed Windows issue on Ascii-executable
;                 Added Flag #TPG_Time_BorderLess for MacOS
;V0.21 : Fixed Flag names and some (now wrong) hints in this description and the code + tidy up a bit
;        Changed for Win: Callback_Window_Main() to Callback_Window() cause now it applies to all windows


;T e r m s:
;GadgetNo: PB-No for the Gadget, like #Gadget (here i.e. #TimeGadget1) or the Return from Gadget creation with #PB_Any
;GadgetId: Pointer to the API control/widget/object like GadgetID(GadgetNo), *widget, ...

;C r e a t e Gadget for Module-Version (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; *GadgetId= TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags)
; P a r a m e t e r s:
; GadgetNo: Can be a valid constant number (but nor necessary) AND #PB_Any
; x,y,w,h : Coordinates as usual
; sTime   : Time as valid string. From 00:00:00 to 23:59:59, for Windows #TPG_Time_12h format too !!! (it's auto converted)
; WindowNo: The PB #Window number (not WindowId or Result)
; Flags   : see below
; -> USE ONLY THE RETURNED POINTER (*GadgetId) FOR ACCESS !!! (here *gTimeGadget1 or *gTimeGadget2)

;A c c e s s for Module-Version (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; Use the pointer (here *gTimeGadget1 or *gTimeGadget2) as GadgetID for access ....
; - GetTimePickerGadgetState(GadgetID);                                        Equivalent to GetGadgetState(#Gadget)
;   : Provides the time as TimeValue
; - SetTimePickerGadgetState(GadgetID, TimeVal);                               Equivalent to SetGadgetState(#Gadget, State)
;   : Set Gadgets time from TimeValue
; - GetTimePickerGadgetText(GadgetID)                                          Equivalent to GetGadgetText(#Gadget)
;   : Provides the time as formated TimeString, #TPG_TimeMask24h: %hh:%ii:%ss, #TPG_Time_12h too
; - SetTimePickerGadgetText(GadgetID, TimeString.s)                            Equivalent to SetGadgetText(#Gadget, Text$)
;   : Set the Gadgets time as formated TimeString, #TPG_TimeMask24h: %hh:%ii:%ss, #TPG_Time_12h too

;F l a g s for creating (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; Linux Gtk:
; - #TPG_Time_ReadOnly                                                         Same effect as #PB_String_ReadOnly on StringGadget
; - #TPG_Time_BorderLess                                                       Same effect as #PB_String_BorderLess on StringGadget
; - (#TPG_Time_NoUpDown)                                                       Always ON
; - (#TPG_Time_12h)                                                            Not yet possible
; Mac:
; - (#TPG_Time_ReadOnly?)                                                      Possible ?
; - #TPG_Time_BorderLess?                                                      Gadget w/o border
; - #TPG_Time_NoUpDown                                                         w/o up/down arrows
; - (#TPG_Time_12h)                                                            Seems not possible ?
; Windows:
; - (#TPG_Time_ReadOnly?)                                                      Seems not possible ?
; - #TPG_Time_BorderLess                                                       Gadget w/o border
; - (#TPG_Time_NoUpDown?)                                                      Seems not possible ?
; - #TPG_Time_12h                                                              12h setting w. attached AM/PM segment.
;    -> Time range in Windows AM/PM ...
;       24h: 00:00:00   = 12h: 12:00:00 AM
;       24h: 00:59:59   = 12h: 12:59:59 AM
;       24h: 01:00:00   = 12h: 01:00:00 AM
;       24h: 12:00:00   = 12h: 12:00:00 PM
;       24h: 12:59:59   = 12h: 12:59:59 PM
;       24h: 13:00:00   = 12h: 01:00:00 PM
;       24h: 23:59:59   = 12h: 11:59:59 PM
;   see https://www.timeanddate.com/time/am-And-pm.html

;A d a p t i o n:
; Win: automated
; Lin: automated
; Mac: automated

;E v e n t s:
; Watch (EventType() on) pointer to TimePickerGadget within PB-WindowNo section. Here *gTimeGadget1 or *gTimeGadget2

;C O N T R O L L I N G:
; Flag or feature                                  Linux      Mac      Windows
; ----------------------------------------------------------------------------
; #TPG_Time_ReadOnly                                 +         -          -
; #TPG_Time_BorderLess                               +         +          +
; #TPG_Time_NoUpDown                               always      +          -
; #TPG_Time_12h                                      -         -          +
; Value scrolling with mousewheel over segment       +         -          -
; Value scrolling with mousewheel over arrows        -         -          +
; Value scrolling with key <up>/<down>               +         +          +
; Value scrolling with num pads <+>/<->              +         -          +
; Wrapping on segments value overrun/undershoot      -         ?          +
; Value jumps to segments max. value               <End>       -        <End>
;                                                                       <Home>
; Value jumps to segments min. value (0)           <Home>      -          -
; Value reset (recover format)                     <Esc>       -          -
; Segment selection with mouse click                 +         +          +
; Segment selection with key <left>/<right>          +         +          +
; Wrapping on segment change                         +         -       <Right>
; Segment forwarding                                 -        <->         -
;                                                             </>
;                                                             <,>

;-Init Demo ...
EnableExplicit

; Object constants
Enumeration Windows
	#Win_Main   = 1;                                                             Set to 1 for tests (because 0 is a empty value too)
; 	#Win_Sec;                                                                    2. Window now with #PB_Any for Demo
EndEnumeration

Enumeration Gadgets
	#TimeGadget1= 1;                                                             1. TimePickerGadget with GadgetNo (not needed any longer!)
	#StGMain1
	#TxGMain1
	#TxMainG2
	#BtGMainSet
	
	;#TimeGadget2;                                                                2. TimePickerGadget now with #PB_Any
	#StGSec1
	#TxGSec1
	#TxGSec2
	#BtGSecSet
EndEnumeration

Global.i gEvent, gEventGadget, gEventWin, gQuit;                               For EventLoop
Global   *gTimeGadget1, *gTimeGadget2;                                         GadgetId (see above, pointer is neccessary)
Global.i gWin_Sec;                                                             Instead of #Win_Sec for #PB_Any on Window 2


;- Time Picker module ...
DeclareModule TimePickerGadget
	
	;-{ DeclareModul: TimePickerGadget ...
	
	;Flags for Gadget creation ...
	#Event_TimePickerChanged = #PB_Event_FirstCustomValue
	#TPG_Time_ReadOnly   = 1 << 8;                                               Time not editable. Only Linux
	#TPG_Time_BorderLess = 1 << 9;                                               Display without border (Mac without funktion)
	#TPG_Time_NoUpDown   = 1 << 10;                                              Display without up/down arrow buttons (Linux always on, Windows not available)
	#TPG_Time_12h        = 1 << 11;                                              Display + edit in 12h format 08:00:00 AM | 08:00:00 PM, only Windows
	
	#TPG_TimeMask24h     = "%hh:%ii:%ss"
	#TPG_DebugOn         = #True;                                                Output in Debug window. Set to #False if it's annoying
	
	CompilerSelect #PB_Compiler_OS
			
		CompilerCase #PB_OS_Linux
			
			ImportC ""
				gtk_widget_has_focus             (*widget.GtkWidget)
				gtk_editable_get_selection_bounds(*entry.GtkEntry, *start_pos, *end_pos)
				gtk_entry_set_text               (*entry.GtkEntry, text.p-utf8)
				gtk_entry_get_text               (*entry.GtkEntry)
				gtk_entry_get_text_length        (*entry.GtkEntry)
				gtk_entry_set_alignment          (*entry.GtkEntry, xalign.f)
				g_signal_connect                 (*instance, detailed_signal.p-utf8, *c_handler, *data, destroy= 0, flags= 0) As "g_signal_connect_data"
			EndImport
			
			;from #define __GDK_KEYSYMS_H__
			#GDK_KEY_Up          = $FF52
			#GDK_KEY_Down        = $FF54
			#GDK_KEY_Left        = $FF51
			#GDK_KEY_Right       = $FF53
			#GDK_KEY_Tab         = $FF09
			#GDK_KEY_Escape      = $FF1B
			#GDK_KEY_KP_Add      = $FFAB
			#GDK_KEY_KP_Subtract = $FFAD
			#GDK_KEY_End         = $FF57
			#GDK_KEY_Home        = $FF50
			#GDK_KEY_KP_0        = $FFB0
			#GDK_KEY_KP_9        = $FFB9
			#GDK_KEY_0           = $0030
			#GDK_KEY_9           = $0039
			
			Structure CALLBACKUSERDATA
				GadgetId.i
				WindowNo.i
			EndStructure
			NewList GadgetData.CALLBACKUSERDATA()
			
		CompilerCase #PB_OS_MacOS
			#NSDatePickerStyleTextFieldAndStepper    = 0
			#NSDatePickerStyleTextField              = 2
			#NSHourMinuteSecondDatePickerElementFlag = 14
			
			Structure TimePickerCellEntry
				ID.I
				WindowNo.I
			EndStructure
			NewList TimePickerCell.TimePickerCellEntry()
			
	CompilerEndSelect
	
	Declare.i GetTimePickerGadgetState(GadgetId)
	Declare   SetTimePickerGadgetState(GadgetId, TimeVal)
	Declare.s GetTimePickerGadgetText (GadgetId)
	Declare   SetTimePickerGadgetText (GadgetId, sTimeString.s)
	Declare   TimePickerGadget        (GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null)
	
	;-} EndDeclareModul: TimePickerGadget ...
	
EndDeclareModule

Module TimePickerGadget
	
	;-{ Modul: TimePickerGadget ...
	
	CompilerSelect #PB_Compiler_OS
			
		CompilerCase #PB_OS_Linux
			
			;-{  _Linux TimePickerGadget API ...
			
			Procedure.i GetTimePickerGadgetState(GadgetId)
				Protected.i TimeVal
				
				TimeVal= ParseDate(#TPG_TimeMask24h, PeekS(gtk_entry_get_text(GadgetId), -1, #PB_UTF8))
				
				ProcedureReturn TimeVal
			EndProcedure
			
			Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
				If TimeVal > -1 And TimeVal < 86400
					gtk_entry_set_text(GadgetId, FormatDate(#TPG_TimeMask24h, TimeVal))
				EndIf
			EndProcedure
			
			Procedure.s GetTimePickerGadgetText(GadgetId)
				Protected.i TimeVal
				Protected.s sTimeString= PeekS(gtk_entry_get_text(GadgetId), -1, #PB_UTF8)
				
				TimeVal= ParseDate(#TPG_TimeMask24h, sTimeString)
				sTimeString= FormatDate(#TPG_TimeMask24h, TimeVal)
				
				ProcedureReturn sTimeString
			EndProcedure
			
			Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
				Protected.i TimeVal
				
				If ParseDate(#TPG_TimeMask24h, sTimeString) > -1
					gtk_entry_set_text(GadgetId, sTimeString)
				EndIf
			EndProcedure
			
			Procedure _TimePickerGadget_SelectPart(*widget, Range, Colon1, Colon2);  internal
				Select Range
					Case 1 : gtk_editable_select_region_(*widget, 0,      Colon1 - 1)
					Case 2 : gtk_editable_select_region_(*widget, Colon1, Colon2 - 1)
					Case 3 : gtk_editable_select_region_(*widget, Colon2, -1)
				EndSelect
			EndProcedure
			
			Procedure _TimePickerGadget_CheckInput(*widget.GtkWidget, EditAction, *user_data.CALLBACKUSERDATA);  internal
;				EditAction: 0 = nothing, -1 = scroll-, 1 = scroll+, -2 = lower limit, 2 = upper limit, 3 = number input
				Protected.i cPos, I
				Protected.i cBlock;                                                    0= out, 1 = h, 2 = m, 3 = s
				Protected.s sPart
				Protected.s sTime=   PeekS(gtk_entry_get_text_(*widget), -1, #PB_UTF8), sTime_
				Protected.i Colon1=  FindString(sTime, ":");                           Find separators on existing string
				Protected.i Colon2=  FindString(sTime, ":", Colon1 + 1)
				Static.s    TimeMax= "23:59:59"
				Protected.i hMax=    Val(StringField(TimeMax, 1, ":"))
				Protected.i mMax=    Val(StringField(TimeMax, 2, ":"))
				Protected.i sMax=    Val(StringField(TimeMax, 3, ":"))
				
				If gtk_widget_has_focus(*widget) And gtk_editable_get_editable_(*widget)
					
					cPos= gtk_editable_get_position_(*widget);                           Get cursor offset
					Select cPos;                                                         Specify segment index on existing time string
						Case 0 To Colon1 - 1      : cBlock= 1
						Case Colon1 To Colon2 - 1 : cBlock= 2
						Case Colon2 To Len(sTime) : cBlock= 3
					EndSelect
					
					If EditAction < 3;                                                   All scroll inputs in segment (up/down, +/-, home/end or mouse scrolling) ...
						
						For I= 1 To 3;                                                     Reformat segments
							sTime_+ RSet(Str(Val(StringField(sTime, I, ":"))), 2, "0") + ":"
						Next I
						sTime = RTrim(sTime_, ":")
						Colon1= FindString(sTime, ":");                                    Correct separators on existing string
						Colon2= FindString(sTime, ":", Colon1 + 1)
						gtk_entry_set_text  (*widget, sTime);                              Reformat string
						_TimePickerGadget_SelectPart(*widget, cBlock, Colon1, Colon2);     Set selected segment
						
						sPart= StringField(sTime, cBlock, ":");                            Read segments value under cursor
						Select EditAction;                                                 Calculate new values from scroll inputs
							Case  1 : sPart= Str(Val(sPart) + 1)
							Case -1 : sPart= Str(Val(sPart) - (Bool(Val(sPart) > 0)))
							Case  2 : sPart= StringField(TimeMax, 2, ":");                   will be clipped per segment further down ...
							Case -2 : sPart= "0"
						EndSelect
						
						If cBlock And (Abs(EditAction) = 1 Or Abs(EditAction) = 2);        Monitor and restrict the new values in the segments....
							
							Select cBlock
									
								Case 1
									If Val(sPart) > hMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
									sTime= RSet(sPart, 2, "0") + ":" + StringField(sTime, 2, ":") + ":" + StringField(sTime, 3, ":")
									gtk_editable_select_region_(*widget, 0,      Colon1 - 1)
									
								Case 2
									If Val(sPart) > mMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
									sTime= StringField(sTime, 1, ":") + ":" + RSet(sPart, 2, "0") + ":" + StringField(sTime, 3, ":")
									gtk_editable_select_region_(*widget, Colon1, Colon2 - 1)
									
								Case 3
									If Val(sPart) > sMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
									sTime= StringField(sTime, 1, ":") + ":" + StringField(sTime, 2, ":") + ":" + RSet(sPart, 2, "0")
									gtk_editable_select_region_(*widget, Colon2, -1)
									
							EndSelect
							gtk_entry_set_text  (*widget, sTime)
							_TimePickerGadget_SelectPart(*widget, cBlock, Colon1, Colon2)
							PostEvent(#PB_Event_Gadget, *user_data\WindowNo, *user_data\GadgetId, #PB_EventType_Change)
							
						EndIf
						
					Else;                                                                Value input in segment ...
						
						If cBlock
							
							sPart= StringField(sTime, cBlock, ":");                          Read segments value under cursor
							
							Select cBlock;                                                   Monitor and restrict the newly entered value in the segments....
									Case 1 : If Val(sPart) > hMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= sPart + ":" + StringField(sTime, 2, ":") + ":" + StringField(sTime, 3, ":") : EndIf
									Case 2 : If Val(sPart) > mMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= StringField(sTime, 1, ":") + ":" + sPart + ":" + StringField(sTime, 3, ":") : EndIf
									Case 3 : If Val(sPart) > sMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= StringField(sTime, 1, ":") + ":" + StringField(sTime, 2, ":") + ":" + sPart : EndIf
							EndSelect
							gtk_entry_set_text(*widget, sTime);                              Reformat string
							gtk_editable_set_position_(*widget, cPos);                       Reset cursor position
							PostEvent(#PB_Event_Gadget, *user_data\WindowNo, *user_data\GadgetId, #PB_EventType_Change)
							
						EndIf
						
					EndIf
					
				EndIf
			EndProcedure; TimePickerGadgetCheckInput
			
			ProcedureC Callback_TimePickerGadget_LostFocus(*widget.GtkWidget, *event.GdkEventFocus, *user_data.CALLBACKUSERDATA)
				gtk_editable_select_region_(*widget, 0, 0)
			EndProcedure
			
			ProcedureC Callback_TimePickerGadget_ClickAction(*widget.GtkWidget, *event.GdkEventButton, *user_data.CALLBACKUSERDATA)
				;Protected.i ModKey= *event\state & $F;                                 reserved for bigger jumps with Shift modifier
				
				If *event\type = #GDK_BUTTON_RELEASE
					_TimePickerGadget_CheckInput(*widget, 0, *user_data)
				EndIf
			EndProcedure
			
			ProcedureC Callback_TimePickerGadget_KeyAction(*widget.GtkWidget, *event.GdkEventKey, *user_data.CALLBACKUSERDATA)
				Protected.i ModKey= *event\state & $F;                                 (Required 'modifier keys' masked with low nibble)
				Protected.i KeyVal= *event\keyval
				Protected.i KeyRelease= *event\type
				Protected.i Stop= #False
				Protected.i EditAction= 0
				Protected.i SelStart, SelEnd
				
				gtk_editable_get_selection_bounds(*widget, @SelStart, @SelEnd);        neu 0.12 ...
				If SelEnd - SelStart > 2;                                              destroy wrong selection
					gtk_editable_set_position_(*widget, 1);                              'SelEnd' instead of '1' sets cursor to selection end - if so wanted
					Stop= #True
				EndIf
				
				If KeyRelease = #GDK_KEY_PRESS;                                        push key or button
					
					If ModKey = #Null;                                                   no modifier key
						
						Select KeyVal
								
							Case #GDK_KEY_KP_0 To #GDK_KEY_KP_9, #GDK_KEY_0 To #GDK_KEY_9, #GDK_KEY_Tab; Digits from main- and numeric pad
								
							Case #GDK_KEY_Up, #GDK_KEY_KP_Add;                               Cursor up, NumPad +
								_TimePickerGadget_CheckInput(*widget,  1, *user_data)
								Stop= #True
								
							Case #GDK_KEY_Down, #GDK_KEY_KP_Subtract;                        Cursor down, NumPad -
								_TimePickerGadget_CheckInput(*widget, -1, *user_data)
								Stop= #True
								
							Case #GDK_KEY_End;                                               Upper limit, End
								_TimePickerGadget_CheckInput(*widget,  2, *user_data)
								Stop= #True
								
							Case #GDK_KEY_Home;                                              Lower limit, Home/Pos1
								_TimePickerGadget_CheckInput(*widget, -2, *user_data)
								Stop= #True
								
							Default;                                                         Stop handler for other keys
								Stop= #True
								
						EndSelect
						
					EndIf
					
				EndIf
				
				If KeyRelease = #GDK_KEY_RELEASE;                                      release key or button
					
					If ModKey = #Null;                                                   w/o modifier key
						
						Select KeyVal
								
							Case #GDK_KEY_Escape;                                            <Esc> for emergency reset. Comment out if unwanted
								gtk_entry_set_text(*widget, "12:00:00")
								
							Case #GDK_KEY_Left;                                              Cursor left
								gtk_editable_set_position_(*widget, SelStart - 1)
								
							Case #GDK_KEY_Right;                                             Cursor right
								SelEnd * Bool(Not(SelEnd = gtk_entry_get_text_length(*widget)));  Cursor position on 0 if behind string end
								gtk_editable_set_position_(*widget, SelEnd + 1)
								
							Case #GDK_KEY_KP_0 To #GDK_KEY_KP_9, #GDK_KEY_0 To #GDK_KEY_9;   Digits from main- and numeric pad
								EditAction= 3
								
							Default;                                                         Stop handler for other keys
								Stop= #True
								
						EndSelect
						
					EndIf
					
					If Not Stop
						_TimePickerGadget_CheckInput(*widget, EditAction, *user_data)
					EndIf
					
				EndIf
				
				ProcedureReturn Stop
			EndProcedure
			
			ProcedureC Callback_TimePickerGadget_ScrollBlock(*widget.GtkWidget, *event.GdkEventScroll, *user_data.CALLBACKUSERDATA)
				Protected.GdkEventKey *eventKey= gdk_event_new_(#GDK_KEY_RELEASE)
				
				*eventKey\window= gtk_widget_get_root_window_(*widget)
				*eventKey\type= #GDK_KEY_PRESS
				If gtk_widget_has_focus(*widget)
					
					If *event\direction = #GDK_SCROLL_UP
						*eventKey\keyval = #GDK_KEY_Up
						Callback_TimePickerGadget_KeyAction(*widget, *eventKey, *user_data)
						
					ElseIf *event\direction = #GDK_SCROLL_DOWN
						*eventKey\keyval = #GDK_KEY_Down
						Callback_TimePickerGadget_KeyAction(*widget, *eventKey, *user_data)
						
					EndIf
					
				EndIf
			EndProcedure
			
			Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null);   Linux
				Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
				Protected.i GadgetId, PBId
				Protected.i _Flags= #Null
				Shared      GadgetData()
				
				If Not MatchRegularExpression(TimePattern, sTime)
					If #TPG_DebugOn
						Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
						Debug " -> Time is set to '00:00:00'!"
					EndIf
					sTime= "00:00:00"
				EndIf
				If TimePattern : FreeRegularExpression(TimePattern) : EndIf
				
				If Flags & #TPG_Time_ReadOnly
					_Flags | #PB_String_ReadOnly
				EndIf
				If Flags & #TPG_Time_BorderLess
					_Flags | #PB_String_BorderLess
				EndIf
				If #TPG_DebugOn And Flags & #TPG_Time_NoUpDown
					Debug "TimePickerGadget Flag: '#TPG_Time_NoUpDown' isn't implemented on Linux."
				EndIf
				If #TPG_DebugOn And Flags & #TPG_Time_12h
					Debug "TimePickerGadget Flag: '#TPG_Time_12h' isn't implemented on Linux."
				EndIf
				
				PBId= StringGadget(GadgetNo, x,  y,  w,  h, sTime, _Flags)
				If GadgetNo = #PB_Any
					GadgetId= GadgetID(PBId)
				ElseIf GadgetNo > #PB_Any
					GadgetId= GadgetID(GadgetNo)
				Else
					Debug "TimePickerGadget [" + Str(GadgetNo) + "]: GadgetNo < #PB_Any not defined! Programm stopped!" : End
				EndIf
				
				AddElement(GadgetData())
				GadgetData()\GadgetId= GadgetId
				GadgetData()\WindowNo= WindowNo
				
				gtk_entry_set_max_length_(GadgetId, 8)
				gtk_entry_set_alignment  (GadgetId, 0.5)
				gtk_widget_add_events_   (GadgetId, #GDK_FOCUS_CHANGE | #GDK_SCROLL_MASK | #GDK_KEY_PRESS_MASK)
				
				g_signal_connect(GadgetId, "scroll-event",         @Callback_TimePickerGadget_ScrollBlock(), @GadgetData())
				g_signal_connect(GadgetId, "button-press-event",   @Callback_TimePickerGadget_ClickAction(), @GadgetData())
				g_signal_connect(GadgetId, "button-release-event", @Callback_TimePickerGadget_ClickAction(), @GadgetData())
				g_signal_connect(GadgetId, "key-press-event",      @Callback_TimePickerGadget_KeyAction(),   @GadgetData())
				g_signal_connect(GadgetId, "key-release-event",    @Callback_TimePickerGadget_KeyAction(),   @GadgetData())
				g_signal_connect(GadgetId, "focus-out-event",      @Callback_TimePickerGadget_LostFocus(),   @GadgetData())
				
				ProcedureReturn GadgetId
			EndProcedure;   Linux
			
			;-}  _Linux TimePickerGadget API END
			
		CompilerCase #PB_OS_MacOS
			
			;-{  _MacOS TimePickerGadget API ...
			
			Procedure GetUTCOffset()
				Shared.i iUTCOffsetSeconds
				Shared.s sUTCOffset
				
				iUTCOffsetSeconds = CocoaMessage(0, CocoaMessage(0, 0, "NSTimeZone systemTimeZone"), "secondsFromGMT")
				
				If iUTCOffsetSeconds < 0
					sUTCOffset = "-" + FormatDate("%hh%ii", 24 * 60 * 60 - iUTCOffsetSeconds)
				Else
					sUTCOffset = "+" + FormatDate("%hh%ii", iUTCOffsetSeconds)
				EndIf
			EndProcedure
			
			Procedure.i GetTimePickerGadgetState(GadgetId)
				Shared.i iUTCOffsetSeconds
				
				Protected   *CurrentDate
				Protected.s sCurrentDate
				Protected.i TimeVal
				
				*CurrentDate= CocoaMessage(0, GadgetId, "dateValue")
				sCurrentDate= PeekS(CocoaMessage(0, CocoaMessage(0, *CurrentDate, "description"), "UTF8String"), -1, #PB_UTF8)
				
				If OSVersion() <= #PB_OS_MacOSX_10_6
					TimeVal= ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8))
				Else
					TimeVal= AddDate(ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)), #PB_Date_Second, iUTCOffsetSeconds)
				EndIf
				
				ProcedureReturn TimeVal
			EndProcedure
			
			Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
				Shared.s sUTCOffset
				
				Protected.s sTime
				
				If TimeVal > -1 And TimeVal < 86400
					sTime = FormatDate("%yyyy-%mm-%dd", Date()) + " " + FormatDate(#TPG_TimeMask24h, TimeVal) + " " + sUTCOffset
					*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTime)
					CocoaMessage(0, GadgetId, "setDateValue:@", @*StartDate)
				EndIf
			EndProcedure
			
			Procedure.s GetTimePickerGadgetText(GadgetId)
				Shared.i iUTCOffsetSeconds
				
				Protected   *CurrentDate
				Protected.s sCurrentDate
				Protected.s sCurrentTime
				
				*CurrentDate= CocoaMessage(0, GadgetId, "dateValue")
				sCurrentDate= PeekS(CocoaMessage(0, CocoaMessage(0, *CurrentDate, "description"), "UTF8String"), -1, #PB_UTF8)
				
				If OSVersion() <= #PB_OS_MacOSX_10_6
					sCurrentTime= FormatDate(#TPG_TimeMask24h, ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)))
				Else
					sCurrentTime= FormatDate(#TPG_TimeMask24h, AddDate(ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)), #PB_Date_Second, iUTCOffsetSeconds))
				EndIf
				
				ProcedureReturn sCurrentTime
			EndProcedure
			
			Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
				Shared.s sUTCOffset
				
				Protected *StartDate
				
				If ParseDate(#TPG_TimeMask24h, sTimeString) > -1
					sTimeString = FormatDate("%yyyy-%mm-%dd", Date()) + " " + sTimeString + " " + sUTCOffset
					*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTimeString)
					CocoaMessage(0, GadgetId, "setDateValue:@", @*StartDate)
				EndIf
			EndProcedure
			
			ProcedureC Callback_TimePickerGadget_Changed(Object.I, Selector.I, Cell.I, ProposedValue.I, ProposedTimeInterval.I)
				Shared TimePickerCell()
				
				ForEach TimePickerCell()
					If TimePickerCell()\ID = Cell
						PostEvent(#PB_Event_Gadget, TimePickerCell()\WindowNo, Cell, #PB_EventType_Change)
						Break
					EndIf
				Next
			EndProcedure
			
			Procedure InitCallback(TimePicker.I)
				Protected AppDelegate.I  = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
				Protected DelegateClass.I= CocoaMessage(0, AppDelegate, "class")
				Protected Selector.I     = sel_registerName_("datePickerCell:validateProposedDateValue:timeInterval:")
				
				class_addMethod_(DelegateClass, Selector, @Callback_TimePickerGadget_Changed(), "v@:@@@")
				CocoaMessage(0, TimePicker, "setDelegate:", AppDelegate)
			EndProcedure
			
			Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null);   MacOS
				Shared.s sUTCOffset
				Shared.i TimePickerCell()
				
				Protected   Frame.NSRect
				Protected   *StartDate
				Protected.i TimePicker
				Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
				
				If sUTCOffset = ""
					GetUTCOffset()
				EndIf
				
				If Not MatchRegularExpression(TimePattern, sTime)
					If #TPG_DebugOn
						Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
						Debug " -> Time is set to '00:00:00'!"
					EndIf
					sTime= "00:00:00"
				EndIf
				If TimePattern : FreeRegularExpression(TimePattern) : EndIf
				
				Frame\origin\x = x
				Frame\origin\y = y
				Frame\size\width = w
				Frame\size\height = h
				TimePicker = CocoaMessage(0, CocoaMessage(0, 0, "NSDatePicker alloc"), "initWithFrame:@", @Frame)
				
				If #TPG_DebugOn And Flags & #TPG_Time_ReadOnly
					Debug "TimePickerGadget Flag: '#TPG_Time_ReadOnly' isn't implemented on Mac."
				EndIf
				If Flags & #TPG_Time_BorderLess
				 	CocoaMessage(0, TimePicker, "setBordered:", #False)
				Else
					CocoaMessage(0, TimePicker, "setBezeled:", #True)
				EndIf
				If Flags & #TPG_Time_NoUpDown
					CocoaMessage(0, TimePicker, "setDatePickerStyle:", #NSDatePickerStyleTextField)
				Else
					CocoaMessage(0, TimePicker, "setDatePickerStyle:", #NSDatePickerStyleTextFieldAndStepper)
				EndIf
				If #TPG_DebugOn And Flags & #TPG_Time_12h
					Debug "TimePickerGadget Flag: '#TPG_Time_12h' isn't implemented on Mac."
				EndIf
				
				CocoaMessage(0, TimePicker, "setDatePickerElements:", #NSHourMinuteSecondDatePickerElementFlag)
				CocoaMessage(0, CocoaMessage(0, WindowID(WindowNo), "contentView"), "addSubview:", TimePicker)
				sTime = FormatDate("%yyyy-%mm-%dd", Date()) + " " + sTime + " " + sUTCOffset
				*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTime)
				CocoaMessage(0, TimePicker, "setDateValue:@", @*StartDate)
				InitCallback(TimePicker)
				AddElement(TimePickerCell())
				TimePickerCell()\ID = CocoaMessage(0, TimePicker, "cell")
				TimePickerCell()\WindowNo = WindowNo
				
				ProcedureReturn TimePickerCell()\ID
			EndProcedure;   MacOS
			
			;-}  _MacOS TimePickerGadget API END
			
		CompilerCase #PB_OS_Windows
			
			;-{  _Windows TimePickerGadget API ...
			
			Procedure GetTimePickerGadgetState(GadgetId)
				Protected.i TimeVal
				Protected   tTime.systemtime
				
				SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
				TimeVal = Val(Str(ParseDate(#TPG_TimeMask24h, RSet(Str(tTime\wHour), 2, "0") + ":" + RSet(Str(tTime\wMinute), 2, "0") + ":" + RSet(Str(tTime\wSecond), 2, "0"))))
				
				ProcedureReturn TimeVal
			EndProcedure
			
			Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
				Protected tTime.systemtime
				
				If TimeVal > -1 And TimeVal < 86400
					SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
					tTime\wYear  = 1970
					tTime\wMonth = 1
					tTime\wDay   = 1
					tTime\wHour  = Hour(TimeVal)
					tTime\wMinute= Minute(TimeVal)
					tTime\wSecond= Second(TimeVal)
					SendMessage_(GadgetId, #MCM_SETCURSEL, 0, @tTime)
				EndIf
			EndProcedure
			
			Procedure.s GetTimePickerGadgetText(GadgetId)
				Protected.s sTimeString
				Protected   tTime.systemtime
				
				SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
				sTimeString= RSet(Str(tTime\wHour), 2, "0") + ":" + RSet(Str(tTime\wMinute), 2, "0") + ":" + RSet(Str(tTime\wSecond), 2, "0")
				
				ProcedureReturn sTimeString
			EndProcedure
			
			Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
				Protected.i TimeVal
				Protected   tTime.systemtime
				
				TimeVal= ParseDate(#TPG_TimeMask24h, sTimeString)
				If TimeVal > -1 And TimeVal < 86400
					SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
					tTime\wYear  = 1970
					tTime\wMonth = 1
					tTime\wDay   = 1
					tTime\wHour  = Hour(TimeVal)
					tTime\wMinute= Minute(TimeVal)
					tTime\wSecond= Second(TimeVal)
					SendMessage_(GadgetId, #MCM_SETCURSEL, 0, @tTime)
				EndIf
			EndProcedure
			
			Procedure Callback_Window(WindowHandle, Msg, wParam, lParam)
				Protected *pnmh.NMHDR
				Protected WindowNo= GetProp_(WindowHandle, "PB_WINDOWID") - 1
				
				Select Msg
						
					Case #WM_NOTIFY
						
						*pnmh = lParam
						
						Select *pnmh\code
								
							Case #DTN_DATETIMECHANGE
								PostEvent(#PB_Event_Gadget, WindowNo, *pnmh\hwndFrom, #PB_EventType_Change)
								
						EndSelect
						
				EndSelect
				
				ProcedureReturn #PB_ProcessPureBasicEvents
			EndProcedure
			
			Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null);   Windows
				Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
				Protected.i GadgetId, PBId
				
				If Not MatchRegularExpression(TimePattern, sTime)
					If #TPG_DebugOn
						Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
						Debug " -> Time is set to '00:00:00'!"
					EndIf
					sTime= "00:00:00"
				EndIf
				If TimePattern : FreeRegularExpression(TimePattern) : EndIf
				
				InitCommonControls_();                                                 ?
				PBId= DateGadget(GadgetNo, x, y, w, h, "", 0, #DTS_TIMEFORMAT);        no other flags accepted
				If GadgetNo = #PB_Any
					GadgetId= GadgetID(PBId)
				ElseIf GadgetNo > #PB_Any
					GadgetId= GadgetID(GadgetNo)
				Else
					Debug "TimePickerGadget [" + Str(GadgetNo) + "]: GadgetNo < #PB_Any not defined! Programm stopped!" : End
				EndIf
				SetWindowCallback(@Callback_Window(), WindowNo)
				
				If #TPG_DebugOn And Flags & #TPG_Time_ReadOnly
					Debug "TimePickerGadget Flag: '#TPG_Time_ReadOnly' isn't available on Windows."
				EndIf
				If Flags & #TPG_Time_BorderLess
					SetWindowLongPtr_(GadgetId, #GWL_EXSTYLE, 0)
					SetWindowPos_(GadgetId, 0, 0, 0, 0, 0, #SWP_NOMOVE | #SWP_NOSIZE | #SWP_FRAMECHANGED); w/o frame
				EndIf
				If #TPG_DebugOn And Flags & #TPG_Time_NoUpDown
					Debug "TimePickerGadget Flag: '#TPG_Time_NoUpDown' isn't available on Windows."
				EndIf
				If Flags & #TPG_Time_12h
					CompilerIf #PB_Compiler_Unicode
						SendMessage_(GadgetId, #DTM_SETFORMATW, 0, @"hh:mm:ss tt");            set time format 12h + am/pm in unicode
					CompilerElse
						SendMessage_(GadgetId, #DTM_SETFORMATA, 0, @"hh:mm:ss tt");            set time format 12h + am/pm in ascii
					CompilerEndIf
				EndIf
				SetTimePickerGadgetText(GadgetId, sTime)
				
				ProcedureReturn GadgetId
			EndProcedure;   Windows
			
			;-}  _Windows TimePickerGadget API END
			
	CompilerEndSelect
	
	;-} EndModul: TimePickerGadget
	;-
EndModule


; Demo for TimePickerGadget() ...
CompilerIf #PB_Compiler_IsMainFile
	
	UseModule TimePickerGadget;                                                  access with UseModule ...
	
	Procedure Create_WinMain()
		OpenWindow(#Win_Main,      100, 100, 800, 170, "TimePickerGadget, All OS - Demo Win 1 ", #PB_Window_SystemMenu)
		TextGadget(#PB_Any,          5,   5, 790,  20, "Set a time in 24h format as 'hh:mm:ss':")
		TextGadget(#PB_Any,         10,  25, 785,  20, "- choose segment (marked block) with <left>/<right> keys or mouseclick.")
		TextGadget(#PB_Any,         10,  45, 785,  20, "- choose segments value w. number keys, <up>/<down> | num-pads <+>/<-> (not Mac) or mouse scrolling not Mac).")
		TextGadget(#PB_Any,         10,  65, 785,  20, "- jump to segments min.-/max.- values with <home>/<end>. (not Mac, Win only max.)")
		
		StringGadget(#StGMain1,    600,  90, 195,  26, "Only for focus tests!")
		
		TextGadget(#PB_Any,          5,  94,  98,  22, "Set time :")
		*gTimeGadget1= TimePickerGadget(#TimeGadget1, 200,  90,  90,  26, "05:33:20", #Win_Main, #TPG_Time_12h)
		
		TextGadget(#PB_Any,          5, 118, 190,  22, "Time set:")
		TextGadget(#PB_Any,          5, 138, 190,  22, "Time value set (=seconds):")
		TextGadget(#TxGMain1,      200, 118,  98,  22, "")
		TextGadget(#TxMainG2,      200, 138,  98,  22, "")
		
		ButtonGadget(#BtGMainSet,  520, 135, 275,  26, "Set time to 8:00:00 from TimeVal")
	EndProcedure
	
	UnuseModule TimePickerGadget;                                                access without UseModule. ...
	
	Procedure Create_WinSec()
		gWin_Sec= OpenWindow(#PB_Any,       100, 300, 800, 100, "TimePickerGadget, All OS - Demo Win 2 ", #PB_Window_SystemMenu)
		StringGadget(#StGSec1,     600,  10, 195,  26, "Only for focus tests!")
		
		TextGadget(#PB_Any,          5,  14,  98,  22, "Set time :")
		*gTimeGadget2= TimePickerGadget::TimePickerGadget(#PB_Any, 200,  10,  90,  26, "11:06:40", gWin_Sec, TimePickerGadget::#TPG_Time_BorderLess)
		
		TextGadget(#PB_Any,          5,  38, 190,  22, "Time set:")
		TextGadget(#PB_Any,          5,  58, 190,  22, "Time value set (=seconds):")
		TextGadget(#TxGSec1,       200,  38,  98,  22, "")
		TextGadget(#TxGSec2,       200,  58,  98,  22, "")
		
		ButtonGadget(#BtGSecSet,   520,  55, 275,  26, "Set time to 16:00:00 from TimeString")
	EndProcedure
	
	Create_WinMain()
	Create_WinSec()
	
	UseModule TimePickerGadget
	
	Repeat
		gEvent=    WaitWindowEvent()
		gEventWin= EventWindow()
		Select gEventWin
				
			Case #Win_Main;                                                          access with UseModule ...
				
				Select gEvent
						
					Case #PB_Event_CloseWindow
						gQuit= #True
						
					Case #PB_Event_Gadget
						gEventGadget= EventGadget()
						
						Select gEventGadget
								
							Case *gTimeGadget1;                                              watch the pointer and read time
								If EventType() = #PB_EventType_Change
									SetGadgetText(#TxGMain1, GetTimePickerGadgetText(*gTimeGadget1))
									SetGadgetText(#TxMainG2, Str(GetTimePickerGadgetState(*gTimeGadget1)))
								EndIf
								
							Case #BtGMainSet;                                                set TimePickerGadget to 08:00:00 by time value
								SetTimePickerGadgetState(*gTimeGadget1, 28800)
								PostEvent(#PB_Event_Gadget, #Win_Main, *gTimeGadget1, #PB_EventType_Change); trigger event for reading
								
						EndSelect
						
				EndSelect
				
	UnuseModule TimePickerGadget
				
			Case gWin_Sec;                                                           access without UseModule ...
				
				Select gEvent
						
					Case #PB_Event_CloseWindow
						gQuit= #True
						
					Case #PB_Event_Gadget
						gEventGadget= EventGadget()
						
						Select gEventGadget
								
							Case *gTimeGadget2;                                              watch the pointer and read time
								If EventType() = #PB_EventType_Change
									SetGadgetText(#TxGSec1, TimePickerGadget::GetTimePickerGadgetText(*gTimeGadget2))
									SetGadgetText(#TxGSec2, Str(TimePickerGadget::GetTimePickerGadgetState(*gTimeGadget2)))
								EndIf
								
							Case #BtGSecSet;                                                 set TimePickerGadget to 16:00:00 by string
								TimePickerGadget::SetTimePickerGadgetText(*gTimeGadget2, "16:00:00")
								PostEvent(#PB_Event_Gadget, gWin_Sec, *gTimeGadget2, #PB_EventType_Change); trigger event for reading
								
						EndSelect
						
				EndSelect
				
		EndSelect
		
	Until gQuit
	
CompilerEndIf
Schönen Sonntag, Charly

PS: Der Sache mit #PB_Any für den WindowNo-Parameter muss ich mich (wie schon bei der GadgetNo) noch mal widmen!
Nachtrag: #PB_Any für den WindowNo-Parameter scheint keine Probleme zu machen. In diesem Fall den global-/shared-Rückgabewert aus OpenWindow() verwenden.
Update: 2018-10-20: V0.21
Zuletzt geändert von Omi am 20.01.2019 08:37, insgesamt 3-mal geändert.
PureBasic Linux-API-Library: http://www.chabba.de
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: "Gadget(s)" für Zeiteingabe gesucht

Beitrag von ccode_new »

Super!
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Antworten