BBC BASIC for Windows
Programming >> User Interface >> Tabs
http://bb4w.conforums.com/index.cgi?board=ui&action=display&num=1479159842

Tabs
Post by KenDown on Nov 14th, 2016, 8:44pm

Hmmmm.

I'm writing a program using tabs (as per the Wiki article http://bb4w.wikispaces.com/Creating+a+tab+control) and it's all working nicely. I click on the different tabs and the right things display on screen.

Now, however, I want to change tabs under control of the program, so that when I click on something in one tab, that tab hides and another tab displays. I can do it easily with

SYS"ShowWindow",!dlg%(3),0
SYS"ShowWindow",!dlg%(0),5

but the second tab is not selected or highlighted or whatever you want to call it. If I click on a third tab, it appears overwritten by dlg%(0), which is not at all nice!

Anyone with any ideas how I can force the second tab to be selected and the first one deselected? I've tried mucking around with the variables currsel% and prevsel% to no avail.
Re: Tabs
Post by DDRM on Nov 15th, 2016, 11:56am

The short answer is no, I don't know...

There is a PROCSetFocus in winlib5A, which you presumably already have loaded. Does that help?

D
Re: Tabs
Post by Zaphod on Nov 15th, 2016, 2:48pm

So each tab has a dialog on it. When changing tabs you close that dialog and open the other, something like this:
Code:
        REM Change to selected tab
        IF CurTab% > -1 PROC_closedialog(Dlg%(CurTab%)) :REM Close old Tab dialog
        PROC_showdialog(Dlg%(TabID%))                   :REM Open new Tab dialog
        CurTab% = TabID%                                :REM update current pointer
 

Then the newly opened dialog default control will have focus.
I think that is what you are asking.

I also found a problem when minimizing the program and restoring it in that the current tab controls are not repainted, no idea why.
I solved this by putting some code in the main loop to detect this situation and reinitialize the current dialog.

Code:
        REM This block is to fix a peculiarity where the current tab dialog does not repaint after being minimized.
        SYS"IsIconic", @hwnd% TO L%       :REM L% non-zero when minimized
        IF L% THEN
          Min%=TRUE                       :REM Window has been minimized
        ELSE                              :REM Window is not minimized now.
          IF Min% PROC_closedialog(Dlg%(CurTab%)): PROC_showdialog(Dlg%(CurTab%))  :REM Just back from being minimized
          Min%=FALSE                      :REM Update flag, effectively a memory of last scanned state.
        ENDIF
 


It may also be necessary to update the data on the current tab as if you had just got to it.
Re: Tabs
Post by KenDown on Nov 15th, 2016, 5:44pm

Actually, no. You don't close any of the dialog boxes! That's the big convenience about tabs: with dialog boxes accessed via buttons or menu options you have to save the contents and close one box, look up another, then return to the first and actually redraw it with the saved contents.

With tabs the dialog boxes stay open and you can skip from one tab to another - one dialog box to another - without saving or redrawing anything. Your data is still there. Wonderfuil!

I'm using the code from the Wiki article and you have this inside the main loop:

IFhTC%>0THEN
SYS"SendMessage",hTC%,&130B,0,0TOcurrsel%
IFcurrsel%<>prevsel%THEN
IFprevsel%>=0SYS"ShowWindow",!dlg%(prevsel%),0
SYS"ShowWindow",!dlg%(currsel%),5
prevsel%=currsel%
PROCtabhelp(currsel%)
ENDIF
ENDIF

It seems to cope with minimising without any problems, but it will not accept automatic changes, as described in my original post.

Re: Tabs
Post by Zaphod on Dec 8th, 2016, 10:23pm

I suppose the original questioner solved his problem, but for others here is an example program. It does not do exactly what the original questioner asked for in that it controls the tabs programmatically from buttons in the main window. Changing tabs from within another tab would be a very unusual Windows action.
Secondly, the MSDN text and examples of dialogs in tabs, does exactly as I suggested earlier, and closes the dialogs as the tab changes, which is different from the Wiki article. Both methods are included here, with the Wiki method left as default.
Code:
      INSTALL @lib$+"WINLIB5A"
      INSTALL @lib$+"WINLIB2A"

      REM!WC
      SW_HIDE = 0
      SW_SHOW = 5
      TCM_SETCURSEL = 4876
      _LOGPIXELSX = 88
      TCIF_TEXT = 1
      TCM_GETCURSEL = 4875
      TCM_INSERTITEMA = 4871
      WS_CLIPCHILDREN = &2000000
      WS_CLIPSIBLINGS = &4000000
      _ICC_TAB_CLASSES = 8

      Width%=300 : REM Dialogue box sizes
      Height%=200
      font%=8
      size%=10000

      SYS "GetDeviceCaps", @memhdc%, _LOGPIXELSX TO Dpi%   :REM Find the screen resolution in use
      Scale = Dpi%/96                                      :REM Scale relative to standard 96 dpi

      REM Items in the main window have to be scaled to match the dialog boxes which change with dpi.
      H1%= FN_button(@hwnd%,"Back",20*Scale,380*Scale,60*Scale,40*Scale,201,0)
      H2%= FN_button(@hwnd%,"Fwd",80*Scale,380*Scale,60*Scale,40*Scale,202,0)

      DIM dlg%(2)
      dlg%(0)=FNdefine_diag1
      dlg%(1)=FNdefine_diag2
      dlg%(2)=FNdefine_diag3

      REM Now to find the size of the dialog so we can match the tab control to it. (If needed)
      REM to do this we have to open a dilaog and measure it as the font chnages the size as well!
      PROC_showdialog(dlg%(0))
      DIM rc{l%,t%,r%,b%}
      rc.l% = 0
      rc.r% = Width%
      rc.t% = 0
      rc.b% = Height%
      SYS "MapDialogRect", !dlg%(0), rc{}
      pixx% = rc.r%-rc.l%
      pixy% = rc.b%-rc.t%
      PROC_closedialog(dlg%(0))  :REM Destroy the dialog once we have done with it.

      DIM icex{dwSize%, dwICC%}
      icex.dwSize% = DIM(icex{})
      icex.dwICC% = _ICC_TAB_CLASSES
      SYS "InitCommonControlsEx", icex{}

      ON ERROR SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : END
      Click% = 0
      ON SYS Click% = @wparam% : RETURN


      REM --------------- Create the ControlTab window: ------------------
      hTC% = FN_createwindow(@hwnd%, "SysTabControl32", "", 0, 0, pixx%, pixy%, 0, WS_CLIPSIBLINGS OR WS_CLIPCHILDREN, 0)
      IF hTC% = 0 ERROR 100, "Couldn't create tab control window"

      PROCAdd_Tab(hTC%,"First",0) : REM send add tab message to controltab window
      PROCAdd_Tab(hTC%,"Second",1) : REM send add tab message to controltab window
      PROCAdd_Tab(hTC%,"Third",2) : REM send add tab message to controltab window

      REM Make the tab control the parent of the dialog box!
      !(dlg%(0)!4+8) = hTC%
      !(dlg%(1)!4+8) = hTC%
      !(dlg%(2)!4+8) = hTC%

      REM Dock window
      DIM rc{l%,t%,r%,b%}
      SYS "GetWindowRect", hTC%, rc{}
      SYS "GetWindowLong", @hwnd%, -16 TO style%
      SYS "SetWindowLong", @hwnd%, -16, style% AND NOT &50000
      SYS "AdjustWindowRect", rc{}, style% AND NOT &50000, 0
      SYS "SetWindowPos", @hwnd%, 0, 0, 0, rc.r%-rc.l%, rc.b%-rc.t%+100,102  :REM allow space (100) for controls in main window.

      REM Needed only if dialogs not closed as tab changes, Wiki method.
      PROC_showdialog(dlg%(2)): SYS"ShowWindow", !dlg%(2), SW_HIDE
      PROC_showdialog(dlg%(1)): SYS"ShowWindow", !dlg%(1), SW_HIDE
      PROC_showdialog(dlg%(0))

      cursel% = -1
      REPEAT
        click% = 0
        SWAP click%,Click%
        CASE click% OF
          WHEN 201: IF cursel% >0 THEN  SYS "SendMessage", hTC%, TCM_SETCURSEL, cursel%-1, 0
          WHEN 202: IF cursel% <2 THEN  SYS "SendMessage", hTC%, TCM_SETCURSEL, cursel%+1, 0
        ENDCASE
  
        SYS "SendMessage", hTC%, TCM_GETCURSEL, 0, 0 TO id%
        IF id% <> cursel% THEN
          REM MSDN preferred method closes dialogs. (not Wiki method).
          REM https://msdn.microsoft.com/en-us/library/windows/desktop/hh298366(v=vs.85).aspx
          REM IF cursel% >= 0 PROC_closedialog(dlg%(cursel%))
          REM PROC_showdialog(dlg%(id%))
          REM cursel% = id%
    
          REM With this Wiki method the focus is not transfered to default dialog control automatically.
          IF cursel% >= 0 SYS"ShowWindow", !dlg%(cursel%), SW_HIDE
          SYS"ShowWindow", !dlg%(id%), SW_SHOW
          cursel% = id%
          SYS "GetDlgItem",!dlg%(id%),101 TO H%
          PROC_setfocus(H%)
    
        ENDIF
      UNTIL INKEY(1) = 0

      END

      REM
      -----------------------------------------------------------------

      DEFPROCAdd_Tab(htc%,text$,id%)
      LOCAL cti{}, fn%, res%
      DIM cti{mask%,dwState%,dwStateMask%,pszText%,cchTextMax,iImage,lparam%}
      text$ += CHR$0
      cti.mask%=TCIF_TEXT
      cti.iImage=-1
      cti.pszText%=!^text$
      SYS "SendMessage", htc%, TCM_INSERTITEMA, id%, cti{} TO res%
      IF res% = -1 ERROR 100, "Couldn't send Tab Control info"
      ENDPROC

      REM
      -----------------------------------------------------------------

      DEF FNdefine_diag1
      LOCAL dlg%
      dlg%=FN_newdialog("",0,14,Width%,Height%,font%,size%)
      dlg%!16=&508800C4 : REM remove title bar and make child window
      PROC_static(dlg%,"Test1",100,84,6,20,20,0)
      PROC_editbox(dlg%,"",101,84,30,50,14,0)
      = dlg%

      DEF FNdefine_diag2
      LOCAL dlg%
      dlg%=FN_newdialog("",0,14,Width%,Height%,font%,size%)
      dlg%!16=&508800C4 : REM remove title bar and make child window
      PROC_static(dlg%,"Test2",100,84,6,20,20,0)
      PROC_editbox(dlg%,"",101,84,30,50,14,0)
      = dlg%

      DEF FNdefine_diag3
      LOCAL dlg%
      dlg%=FN_newdialog("",0,14,Width%,Height%,font%,size%)
      dlg%!16=&508800C4 : REM remove title bar and make child window
      PROC_static(dlg%,"Test3",100,84,6,20,20,0)
      PROC_editbox(dlg%,"",101,84,30,50,14,0)
      = dlg%
 


I hope that might be useful for someone. The automatic scaling with dpi has NOT been tested, but i believe it should work.
Re: Tabs
Post by KenDown on Dec 9th, 2016, 06:38am

Oh brilliant! That does exactly what I want. It was the

SYS"SendMessage", hTC%, 4876, cursel%, 0

which did it, and no, one doesn't need to close any dialog boxes. Just determine the tab number that you need to open and set cursel% to that, send the message and the required tab opens (which automatically obscures the others).
Re: Tabs
Post by Zaphod on Dec 9th, 2016, 3:16pm

Good, I am glad that was useful.

That was:

SYS"SendMessage", hTC%, TCM_SETCURSEL, cursel%, 0

for those that don't memorize the Windows Constants by number.