5

I am looking into GUI development with Racket. I would like to create a tab-panel%, with multiple tabs. The documentation says, that a switch of tab does only call a procedure and does not make a content change happen automatically. I think this is quite clever behavior, but I have a problem implementing an initially empty tab-panel, which only gets content (children) when I select one of the tabs.

This is the code I already have:

#lang racket/gui

(require racket/gui/base)

(define nil '())

(define application-frame
  (new frame%
    [label "Example"]
    [width 400]
    [height 300]))

(define menu-bar
  (new menu-bar%
    [parent application-frame]))

(define file-menu
  (new menu%
    [label "&File"]
    [parent menu-bar]))

(new menu%
  [label "&Edit"]
  [parent menu-bar])

(new menu%
  [label "&Help"]
  [parent menu-bar])

(new menu-item%
  [label "E&xit"]
  [parent file-menu]
  [callback
    (λ (m event)
      (exit nil))])

(define tab-panel
  (new tab-panel%
    [parent application-frame]
    [choices '("&Lookup" "&Training")]
    [callback
      (λ (tp event)
        (case (send tp get-item-label (send tp get-selection))
          [("&Lookup")
            (send tp change-children
              (λ (children)
                (list lookup-panel)))]
          [("&Training")
            (send tp change-children
              (λ (children)
                (list training-panel)))]))]))

(define get-lookup-panel
  (lambda (children)
    (let
      [(lookup-panel (new panel% [parent tab-panel]))]
      [(new message%
        [parent lookup-panel]
        [label "The content of the lookup panel for the lookup tab."])
      lookup-panel])))

(define lookup-panel (new panel% [parent tab-panel]))
(define lookup-panel-content
  (new message%
    [parent lookup-panel]
    [label "The content of the lookup panel for the lookup tab."]))

(define training-panel (new panel% [parent tab-panel]))
(define training-panel-content
  (new message%
    [parent training-panel]
    [label "The content of the training panel for the training tab."]))

(define status-message
  (new message%
    [parent application-frame]
    [label "No events so far..."]
    [auto-resize #t]))

(send application-frame show #t)

The problem here is, that initially both children of the tab-panel are visible, although (naturally) only one tab is selected. When I change tab, the behavior is corrected by the lambdas inside the case form.

However, I cannot simply give those panels, which I set as children no parent, because racket will tell me, that I need to specify the required initial argument parent. This means they will be initially added to the tab-panel. Is it necessary to create the panels and then again remove them from the tab-panel? That would seem a bit dirty. I think there is probably a better way.

I already tried to dynamically create the panel, as can be seen in the get-lookup-panel procedure, but I couldn't get that to work in the case form.

What is the correct way to implement it?

edit1

I found a way to define a procedure, which can be used in the way I want to use it:

(define (get-lookup-panel4 children)
  (define lookup-panel (new panel% [parent tab-panel]))
  (define lookup-panel-message (new message% [parent lookup-panel] [label "LOOKUP"]))
  (list lookup-panel))

Which can be used as follows:

(define tab-panel
  (new tab-panel%
    [parent application-frame]
    [choices '("&Lookup" "&Training")]
    [callback
      (λ (tp event)
        (case (send tp get-item-label (send tp get-selection))
          [("&Lookup")
            (send tp change-children get-lookup-panel4)]
          [("&Training")
            (send tp change-children
              (λ (children)
                (list training-panel)))]))]))

But I don't understand what the difference between this procedure and the other one with the let expression is and another problem with this approach is, that I cannot afterwards modify the created panel or message, because their scope is the procedure.

Zelphir Kaltstahl
  • 5,722
  • 10
  • 57
  • 86
  • The approach I used in my browser was to have a list of custom objects where each object can provide the tab label upon request and can request the object composed of the tab-panel% and several other objects to re-read its label. This might not solve your problem though, as my browser never allows less than 1 tab (and closing the last tab closes the window. The window always starts with >= 1 tab.). – Lazerbeak12345 Apr 27 '21 at 21:13

0 Answers0