Announcement

Collapse
No announcement yet.

Lost tab key

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Lost tab key

    Having changed from using secondary dialogs with parent "0" (i.e., the desktop) to have the
    main dialog as parent brought many improvements in screen behaviour, but it also semi-disables
    the tab key when one of the child dialogs is active. With parent at zero, the tab key responded
    normally; with main dialog as parent, no matter where on the screen an edit box has the focus,
    the tab key takes focus to the first edit control on the screen, and further depressions of TAB
    does nothing other than to make the highlighted (dark blue) area in this first edit control
    flash.

    All these edit controls are subclassed, in order ONLY to trap and respond to the ENTER key and
    the cursor movement keys, and this works as before.

    Does one now have to trap the TAB key and provide code to change focus in a logical way?

    Any help will be greatly appreciated.

    ------------------

  • #2
    It sounds like your "child" dialogs are actually %WS_CHILD dialogs when that are probably ment to be "stand-alone" modal dialogs. If this is the case, don't use %WS_CHILD style for your secondary dialogs.

    In general, %WS_CHILD dialogs are used for such tasks as "pages" on tab controls. In this case, you need to use additional styles (%DS_CONTROL for the child dialog, and the %WS_EX_CONTROLPARENT extended style for the parent dialog).

    If this does not help, please post the DIALOG NEW and DIALOG SHOW statements you are using...

    Thanks!




    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>
    Lance
    mailto:[email protected]

    Comment


    • #3
      I just did the same thing for a job, and yes, you have to trap the tab key to change the focus in a intelligent way.


      There is an API call to help, though. Here's my KB callback function. :
      (I did not want to trap the <Enter> key). Also, you don't want to switch the focus to a Window which is currently disabled.


      Code:
      FUNCTION KBProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
                        BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
       ' procedure used for subclassing buttons and edit controls to move the focus on the
       ' receipt of a TAB key; we just go to the next WS_TABSTOP location
       ' uses the globals gButtonProc, gEditProc
           LOCAL NexthWnd AS LONG, ThishWnd AS LONG, ProcAddr AS DWORD, CtrlID AS LONG
      
           IF wMsg = %WM_KEYDOWN  THEN
               IF wParam = %VK_TAB THEN
                  ThisHwnd = hWnd
                  DO
                    NexthWnd = GetNextDlgTabItem (GetParent(hWnd), ThishWnd, 0)   ' find next
                    IF ISWindowEnabled (NexthWnd) THEN
                       EXIT DO
                    ELSE
                       ThisHwnd = NextHwnd
                    END IF
                  LOOP
                  SetFocus NexthWnd
               END IF
           END IF
      
      ' call original WndProc
            CtrlID = GetWindowLong (hWnd, %GWL_ID)
            SELECT CASE CtrlId
               CASE %IDC_BROWSE_INPUT, %IDC_BROWSE_REPORT, %IDC_BROWSE_OUTPUT, %IDC_PROCESS, %IDC_VIEW_REPORT
                  ProcAddr = gButtonProc
               CASE %IDC_EDIT_INPUT, %IDC_EDIT_OUTPUT, %IDC_EDIT_REPORT
                  ProcAddr = gEditProc
            END SELECT
            FUNCTION = CallWindowProc (Procaddr, hwnd, wMSg, wparam,lparam)
      
      END FUNCTION
      MCM


      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        I would rather place an IsDialogMessage() call in message pump loop... that way, Windows will deal with your Tab movements automatically for you. IsDialogMessage() can be used with conventional SDK-app's as well as (SDK-based) dialogs! If you are using DDT, Tab Control operation is handled for you anyway.

        Dieny's problem sounds more like a problem with window implementation, rather than using some form of customized tab-key handling.

        My $0.02.


        ------------------
        Lance
        PowerBASIC Support
        mailto:[email protected][email protected]</A>
        Lance
        mailto:[email protected]

        Comment


        • #5
          If the problem can be overcome in the STYLE area that would greatly simplify matters,
          Michael. One would also require a bit more code to trap SHIFT-tab as well.

          Lance, here is the code you mention.

          The main dialog is created in the WinMain of the Exe module:

          Code:
          Local Style As Long
          
           Style = %WS_SYSMENU Or _
                   %WS_MINIMIZEBOX Or _
                   %DS_SETFOREGROUND
          
           Dialog New 0, "peAc MAIN MENU", 0, 0, 534, 372, Style, %WS_EX_CONTROLPARENT To pDlg&
          
           Call WndProc (pDlg&, %WM_CREATE, 4, 2)
           Menu Attach hMenu, pDlg&
          (the WndProc is from (I think) Skeleton.Bas, and is not the callback function. It sets
          up the toolbar only).

          It is displayed at the end of WinMain:

          Code:
          Dialog Show Modal pDlg&, Call DlgProc
           Do
            wMsg& = GetMessage (Msg, %NULL, 0, 0)
            TranslateMessage Msg
            DispatchMessage Msg
            Loop While wMsg&
           Function = Msg.wParam
           Exit Function
           End Function
          (the Exit Function is superfluous -- a left-over from early struggles)

          The secondary dialog is created in a DLL, by ---

          Code:
            Style& = %DS_SETFONT Or _
                     %DS_NOFAILCREATE Or _
                     %DS_3DLOOK   Or _
                     %DS_SETFOREGROUND Or _
                     %DS_CONTROL
          '           %WS_CHILD
          
            Dialog New xDlg&, t$, 2, 22, 526, 316, Style& To hDlg&
          (where xDlg& is the handle of main dialog pDlg& which is passed to the DLL)

          and displayed ---

          Code:
          Dialog Show Modeless hDlg& Call RepCamerCallback
           Function=hDlg&
           End Function
          This being in a DLL, it has no WinMain: does the message pump in the main module handle
          Windows messages when this sub-module is the active window? If so, the DLL would have to
          call a function in the main (exe) module to get these. In a nutshell, WHAT can be done
          in a DLL module to simulate a message pump, if Windows only calls the WinMain in the
          main module?

          By substituting %DS_CONTROL for %WS_CHILD above, the TAB key is now quite dead.




          ------------------

          Comment


          • #6
            Your are providing your own low-level message pump, so that is there you need to insert the IsDialogMessage() call (ie, inside the message pump loop) which should enable tab key operation to work properly. This is exactly as I anticipated at the start of the thread.

            Firstly, a message pump only works on a per-thread basis.

            So, if there was a message pump running in the main module (in the same thread context), then your DLL pump is likely to be temporarily "replacing" the main pump (for the duration of that pump's operation) - however, it is not possible to tell conclusively without seeing the entire code for both the DLL and the main module, since it is not clear how your dialog code is being launched from the main module.

            The easiest way to test this would be to remove the DLL's message pump (to see if messages are still being pumped from elsewhere in the thread), but since you are writing the code, you'll already know what is going on without the need to experiment!

            Finally, there is NEVER any need nor any reason to _send_ or generate your own %WM_CREATE message for the window procedure (callback). %WM_CREATE is used at the window class level, not the dialog level.

            That is, the callback will automatically receive a %WM_INITDIALOG message, which is the dialog equivalent of %WM_CREATE. It is in that callback where you sould place your initialization code.

            Similarly, (for the benefit of the lurkers) there you should never generate your own %WM_PAINT message either... explicitly generating such non-queued messages is a Windows GUI programming no-no.

            I hope this helps!


            ------------------
            Lance
            PowerBASIC Support
            mailto:[email protected][email protected]</A>
            Lance
            mailto:[email protected]

            Comment


            • #7
              Guess I didn't read the question very well: I saw "%WS_CHILD" and "subclass" and assumed this was NOT a dialog (CreateDialog or DialogBox API call), and that the problem dealt with "SDK-style" programming.

              If you use CreateWindow to add child controls, even IsDialogMessage does not handle the focus shifting.

              Might help if posters indicate "SDK-style" or "DDT-style" when posting..

              MCM
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                Sorry Michael, but you are wrong... IsDialogMessage() is designed for all types of windows, including CreateWindow(), Dialogs and DDT.

                The "problem" is that the name of the API implies that it does not work for classic (CreateWindow()) windows, but the truth is otherwise.

                ------------------
                Lance
                PowerBASIC Support
                mailto:[email protected][email protected]</A>
                Lance
                mailto:[email protected]

                Comment


                • #9
                  Lance, I have tried both these combinations, but to no avail, in fact, it makes
                  absolutely no difference at all.

                  Code:
                  Dialog Show Modal pDlg&, Call DlgProc
                  
                   Do
                    wMsg& = GetMessage (Msg, %NULL, 0, 0)
                  ' If IsTrue(DLLdlg&) And IsTrue(IsDialogMessage(DLLdlg&, Msg)) Then
                  '   Dialog DoEvents
                  '  Else
                    If IsFalse(DLLdlg&) Or IsFalse(IsDialogMessage(DLLdlg&, Msg)) Then
                     TranslateMessage Msg
                     DispatchMessage Msg
                     End If
                    Loop While wMsg&
                   Function = Msg.wParam
                  One of these child dialogs has a standard scrollbar, which worked when its parent was
                  zero, but now its is dead.

                  What IS strange is that the subclassed function in the DLL does receive all
                  keystrokes, in cluding TAB -- I tested this with a beep at the top of the
                  KeyUp/KeyDown cases. The ENTER key and the cursor movement (arrows &c.) keys all
                  behave normally. Just the TAB key is stone dead.

                  I have looked through Petzold, but did not find anything to clarify the concepts
                  behind this message pump in the main dialog. Whilst the secondary dialog has the
                  focus, does the message pump of the main dialog remain active? For the above
                  code to succeed it would seem so. I have no message pump in the child dialog.
                  Where would it go if one were to have one? There is no WinMain in the child code.

                  Maybe Windows has broken mey keyboard ...


                  ------------------

                  Comment


                  • #10
                    I can assure you that when the dialogs are implemented correctly, IsDialogMessage() works - I use it in my own apps.

                    BTW, Don't place a DIALOG DOEVENTS call in the message pump - essentially a DIALOG DOEVENTS statement calls a message pump, so you would be simply embedding a message pump within a message pump.

                    Ok, I think I see what your problem is:

                    1. Your initial dialog is being called with DIALOG SHOW..MODAL. The message pump seems to follow this, so it is NEVER executed until the MODAL dialog is dismissed. Ouch!
                    2. Your MODELESS dialog is not providing a message pump, but the message pump of the modal dialog is doing the work. However, this should not stop tab control actions from working.

                    Solutions:

                    1. Forget the message pump that occurs immediately after DIALOG SHOW..MODAL. It is irrelevent.
                    2. ??? We need to see rest of your code to see what events are triggering the modeless dialog, etc.



                    ------------------
                    Lance
                    PowerBASIC Support
                    mailto:[email protected][email protected]</A>
                    Lance
                    mailto:[email protected]

                    Comment


                    • #11
                      OK, Lance, I have removed that message pump code, with no immediate change of behaviour
                      noticeable.

                      You do not mention anything about message pumps in DLL modules which create modeless
                      dialogs. Just give me the chapter number in Petzold edition 5 ...

                      As to the sequence of events in the code. After the "show modal" of the main dialog,
                      one selects from a dropdown menu of which the PopUp ID is %CZprecam, and in the main
                      dialog callback triggers this ---

                      Code:
                      Case %CZprecam
                         FCopt&=2
                         Call RemoveSubMenu
                         Call RemoveMainMenu
                         ScrnName$="Camera setup"
                         DLLdlg&=CszRepCamer (pDlg&)
                      The function CszRepCamer is in a DLL, and essentially consists of --

                      Code:
                      Function CszRepCamer Alias "CszRepCamer" (ByVal Dlg&) Export As Long
                       xDlg&=Dlg&
                       RecLen&=460
                       sequ$=      Str$(%CmL11Txt)+Str$(%CmL21Txt)+Str$(%CmL31Txt)+Str$(%CmL41Txt)+Str$(%CmL51Txt)+Str$(%CmL61Txt)
                      
                      '(and quite a lot more in this vein, but the global xDlg& now holds the handle of the MAIN
                      ' dialog)
                       Rtp$="REPCAMER"
                      
                       frf&=FreeFile
                      
                       Open kdd$+"REPCAMER" For Binary As frf& Base=0
                      
                       Seek frf&,1
                       Get$ frf&, 460, setup$
                       Close frf&
                       Call SetupReproDialog
                       Control Send hDlg, %RpRteTxt, %EM_SETSEL, 0, -1
                       Control Set Focus hDlg&, %RpRteTxt
                       Dialog Show Modeless hDlg& Call RepCamerCallback
                       Function=hDlg&
                       End Function
                      DLLdlg& now holds the handle of the global (in the DLL) hDlg& created below.

                      The sub SetupReproDialog which is called starts like this --

                      Code:
                      Sub SetupReproDialog
                      
                       Local icc       As INIT_COMMON_CONTROLSEX
                       Local iccp      As INIT_COMMON_CONTROLSEX
                       Local tie       As TC_ITEM
                       Local tieP      As TC_ITEM
                       Local sTmp      As String
                       Local iItem     As Long
                       Local result    As Long
                       Local lLoop     As Long
                       Local lLoop1    As Long
                      
                       HrsChg&=0
                       chk$=""
                      I include that, in case those items concerned with tab controls may have bearing, though
                      the present sequence does not involve any tab controls.

                      Now the secondary dialog is created

                      Code:
                      If Rtp$<>"REPDSCAN" Then
                      
                        Style& = %DS_SETFONT Or _
                                 %DS_NOFAILCREATE Or _
                                 %DS_3DLOOK   Or _
                                 %DS_SETFOREGROUND Or _
                                 %DS_CONTROL
                      '%WS_CHILD
                      
                        Dialog New xDlg&, t$, 2, 22, 526, 316, Style& To hDlg&
                        End If
                      
                       Select Case Rtp$
                      (Whether the style is %DS_CONTROL or %WS_CHILD makes no apparent difference, so I'm not
                      sure which is correct.)


                      Code:
                      Case "REPCAMER"
                        Control Add Frame, hDlg&, %ReproFrm, t$,          8,   4, 510, 306
                        Control Send hDlg&, %ReproFrm, %WM_SETFONT, qhFont&, 1
                        Call CreateSetupHeading
                        Call GetSubDepartments
                      
                        t$="Budgeted rate"
                        Control Add Frame, hDlg&, %RpRteFrm, t$,        382,  13, 130,  26
                        Control Send hDlg&, %RpRteFrm, %WM_SETFONT, shFont&, 1
                      
                        t$="Camera rate per hour"
                        Control Add Label, hDlg&, %RpRteLbl, t$,        386,  24,  82,  12, %SS_RIGHT
                        Control Send hDlg&, %RpRteLbl, %WM_SETFONT, smFont&, 1
                        Control Add TextBox, hDlg&, %RpRteTxt, "",      470,  23,  34,  12, %ES_RIGHT Or _
                                                                                            %WS_TABSTOP, _
                                                                                            %WS_EX_CLIENTEDGE
                        Control Send hDlg&, %RpRteTxt, %WM_SETFONT, smFont&, 1
                        hCtl&=GetDlgItem(hDlg&, %RpRteTxt)
                        MainWndProc& = SetWindowLong(hCtl&, %GWL_WNDPROC, CodePtr(SetupSubProc))
                      
                        t$="Camera shots: time per shot, cost of film per sheet and contacting time"
                        Control Add Frame, hDlg&, %CmDtlFrm, t$,         14,  74, 498,  231
                        Control Send hDlg&, %CmDtlFrm, %WM_SETFONT, shFont&, 1
                      --followed by hundreds of lines to add labels, 167 textboxes and a few frames, and ending the
                      case selected with

                      Code:
                      RepTme$=""
                      
                        For i&=%CmL11Txt To %CmF67Txt
                         hCtl&=GetDlgItem(hDlg&, i&)
                         MainWndProc& = SetWindowLong(hCtl&, %GWL_WNDPROC, CodePtr(SetupSubProc))
                         If Instr(RepCst$,Str$(i&))=0 Then RepTme$=RepTme$+Str$(i&)
                         Next
                        Call CompleteRepCamerForm
                      
                       Case "REPDSCAN"
                      The sub called at the end is quite small:

                      Code:
                      Sub CompleteRepCamerForm
                       Control Set Text hDlg&, %RpDscTxt, Trim$(Left$(setup$,40))
                       Control Set Text hDlg&, %RpDptTxt, Trim$(Mid$(setup$,43,30))
                       Control Set Text hDlg&, %RpfncTxl, Vo(Mid$(setup$,41,2),4,0)
                       Control Set Text hDlg&, %RpmdtTxl, Trim$(fdt(Mid$(setup$,114,3)))
                       t$=Trim$(Mid$(setup$,74,40))
                       t$=Remove$(t$,Chr$(0))
                       t$=Left$(t$,Instr(LCase$(t$),"ref.")-1)
                       t$=Trim$(t$)
                       t$=Rtrim$(t$,",")
                       Array Scan sdp$(1) For sdp&, From 1 To Len(t$), Collate Ucase, =t$, To x&
                       If x&=0 Then x&=1
                       ComboBox Select hDlg&, %RPsdpCbx, x&
                       Control Set Text hDlg&, %RpRteTxt, Vo(Mid$(setup$,117,3),0,2)
                       m$=Trim$(sequ$)+" "
                      
                       For i&=120 To 436 Step 2
                        id&=Val(m$)
                        m$=Right$(m$,Len(m$)-Instr(m$," "))
                        t$=Vo(Mid$(setup$,i&,2),0,2)
                        If Instr(RepCst$,Str$(id&))=0 Then t$=HrsMns(t$)
                        Control Set Text hDlg&, id&, t$
                        Next
                       End Sub
                      None of this sub (other than setting text) has any reference to GUI. Fdt is "FormatDate" and
                      Vo is "ValueOf" (disk file data contained in binary form modulo 256, for compactness when
                      this code was originally done for DOS and the hard disk (a BIG one) was 40Mb.

                      The subclass procedure SetupSubProc now contains code added to MAKE the tab key respond;
                      without this code it is totally dead, and if style is %WS_CHILD instead of %DS_CONTROL it
                      takes the focus to the first textbox and then does no more.

                      Code:
                      Function SetupSubProc (ByVal hWnd&, ByVal wMsg&, ByVal wParam&, ByVal lParam&) As Long
                      
                       Local bfr As Asciiz * 20
                      
                       Select Case wMsg&
                      
                       Case %WM_KEYDOWN
                        Ci&=GetDlgCtrlID(hWnd&)
                        If wParam&=%VK_TAB Then                          'This uses the global Lkey& to distinguish
                         hCtl&=GetDlgItem(hDlg&, Ci&)                    'between TAB and SHIFT+TAB
                         If Lkey&=%VK_SHIFT Then
                          SendMessage hCtl&, %WM_KEYDOWN, %VK_LEFT, 0    ' (that hCtl& should simply be hWnd&)
                         Else
                          SendMessage hCtl&, %WM_KEYDOWN, %VK_RIGHT, 0
                          End If
                         End If
                        If wParam&=%VK_SHIFT Then Lkey&=wParam&          ' end of work-around, except one referece
                        If Ci&<>%RPdscTxt Then Function=0                ' to Lkey under KEYUP 
                      
                        Select Case wParam&
                      
                        Case %VK_HOME
                         If Ci&=%RPdscTxt Then Exit Select
                         Control Set Focus hDlg&, %RPdscTxt
                      
                        Case %VK_END
                         If Ci&=%RPdscTxt Then Exit Select
                         Select Case Rtp$
                      
                         Case "REPBROMI"
                          Control Set Focus hDlg&, %Br1c6Txt
                      
                         Case "REPCAMER"
                          Control Set Focus hDlg&, %CmF67Txt
                      
                         Case "REPDSCAN"
                      -- and lots more to handle the arrow and other cursor movement keys, plus --

                      Code:
                       Case %WM_KEYUP
                        Ci&=GetDlgCtrlID(hWnd&)
                        GetKeyNameText lParam&, Bfr, 20
                        If bfr<>"Tab" Then Lkey&=0
                      
                        If Trim$(bfr)="Esc" Then
                         Control Handle hDlg&, %RPextBtn To hCtl&    'again should simply be hWnd&
                         SendMessage hCtl&, %BM_CLICK, 0, 0          'however, not relevant
                         Exit Select
                         End If
                      
                        If Instr(bfr,"Enter")>0 Then
                         Ci&=GetDlgCtrlID(hWnd&)
                      
                         Select Case Rtp$
                      
                         Case "REPBROMI"
                      
                          Select Case Ci&
                      
                          Case %RPdscTxt
                           Control Set Focus hDlg&, %RpSdpCbx         'These trigger KILLFOCUS which updates
                                                                      'any changes to disk.
                          Case %RpRteTxt
                           Control Set Focus hDlg&, %BrDrtTxt
                      
                          Case %BrDrtTxt
                           Control Set Focus hDlg&, %Br6e1Txt
                      Hope this casts light without shadow.

                      ------------------

                      Comment


                      • #12
                        When I initially raised the point of the tabkey being unresponsive when a dialog
                        becomes a child of the main dialog (as opposed to a popup on the desktop) Michael Mattias's
                        comment was --

                        >I just did the same thing for a job, and yes, you have to trap the tab key to change the focus >in an intelligent way.

                        This snippet from Win32AAPI Help, on extended styles --

                        WS_EX_CONTROLPARENT Allows the user to navigate among the child windows of the window by using the TAB key.

                        -- appears to confirm that. If there is only one child dialog, the tab key keeps on going to
                        the first edit control of this child dialog that is enabled.

                        It does not explain why a standard scroll bar in such a child window also dies, whereas a
                        scroll bar control behaves normally.


                        ------------------

                        Comment

                        Working...
                        X