Architecture of switching between page panels

For my Windows activation / registration app, I have around 10 different panels that the user moves through during the process. I figure a PagePanel with ContainerControls probably makes the most sense.

In my working MacOS / Cocoa Objective-C++ version, I have a central NSWindowController that adds/removes NSViewControllers as different pages are required. It also handles transitions between the panels, and resizes the window to accommodate the size of the contained panel.

Is that architecture (or a similar architecture) viable with Xojo? I realize I CAN ask the Window to change the PagePanel without dealing with adding/removing controllers. Note that the choice of the next panel to display is sometimes invoked programmatically (for example, an error might result in displaying an error panel).

Should I create a custom event for changing the panel, so I can invoke that event from any place in my code? If so, would the Window containing the PagePanel service that event?

NOTE: I’m saying ā€œeventā€ because I’m concerned that directly calling my own method to change the panel could result in nesting a lot of code – eg a request to change panels MIGHT result in another request to change panels. If events are queued, then an event SHOULD provide some isolation from accidental recursion.

I may have misunderstood what you want, as it sounds simple…

The Pagepanel control will handle all of this for you.
Give it 10 panels, and place on each , the controls you need to display.

All the window has to do is set the ā€˜visible panel’
The remainder will be hidden.

In the Layout Editor, you navigate among panels in a Page Panel control using the widget at the bottom of the control

Get or set the SelectedPanelIndex

2 Likes

My app has six or seven panels on the pagepanel. Switching between them is trivial. You don’t need to create anything, just set the SelectedPanelIndex, as @Jeff_Tullin says.

Note that the widget for switching panels that he refers to is used only in the IDE when you are designing the setup. It doesn’t exist at runtime.

1 Like

@Jeff_Tullin –

All the window has to do is set the ā€˜visible panel’

Absolutely, but if I want to invoke a transition from one page panel to another, I probably should do that from a centralized method. (like resizing the enclosing window to match the size of the visible panel)

Thanks – I’ll give it a try and see if that works as simply as you suggest.

From the docs:

The PanelChanged event handler is called when the SelectedPanelIndex property changes.

Although I have to say that a window which changes size because you switch panels might be a bit unnerving…

1 Like

If each panel may contain data that should be saved when switching to another, then yes, one method to effect that is useful. Seems I call it from 21 places in my app. Looks like I don’t use the PanelChanged event handler at all.

That’s been the interface for MacOS System Preferences for a decade. Also, take a look at the Xojo Options window (Edit > Options) – although Xojo’s Options window only changes HEIGHT, not width, and doesn’t animate between heights.

This will animate the window during a resize, on macOS, and just resize the window on Windows and Linux

Protected Sub WindowResizeAnimated(inWindow as DesktopWindow, width as Integer, height as Integer)
  #if TargetCocoa
    // If you copy this method, you will need the following structure: NSRect
    
    Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
    Declare Function NSSelectorFromString Lib "Cocoa" (aSelectorName As CFStringRef) As Ptr
    Declare Function RespondsToSelector Lib "Cocoa" Selector "respondsToSelector:" (NSWindow As Ptr, aSelector As Ptr) As Boolean
    Declare Function Frame Lib "Cocoa" Selector "frame" (NSWindow As Ptr) As NSRect
    Declare Sub SetFrameDisplayAnimate Lib "Cocoa" Selector "setFrame:display:animate:" (NSWindow As Ptr, inNSRect As NSRect, Display As Boolean, Animate As Boolean)
    
    DIM FrameSelector As Ptr = NSSelectorFromString("frame")
    DIM SetFrameDisplayAnimateSelector As Ptr = NSSelectorFromString("setFrame:display:animate:")
    
    if (RespondsToSelector(inWindow.Handle, FrameSelector)) AND (RespondsToSelector(inWindow.Handle, SetFrameDisplayAnimateSelector)) then
      DIM deltaWidth As CGFloat = width - inWindow.Width
      DIM deltaHeight As CGFloat = height - inWindow.Height
      
      DIM frameRect As NSRect = Frame(inWindow.Handle)
      frameRect.h = frameRect.h + deltaHeight
      frameRect.Y = frameRect.Y - deltaHeight
      frameRect.w = frameRect.w + deltaWidth
      
      SetFrameDisplayAnimate inWindow.Handle, frameRect, TRUE, TRUE
    end if
    
  #else
    me.Width = width
    me.Height = height
  #endif
End Sub
Private Structure NSRect
  x as Double
  y as Double
  w as Double
  h as Double
End Structure
2 Likes

@Emily-Elizabeth_Howard – thanks. That’s the Cocoa method I’m using in my C++ code:

[self.window setFrame:newWindowFrame display:YES animate:YES];

…but it’s cool to see how it could be called via Xojo! Thanks!

This Xojo utility I’m writing is going to be Windows only. I had hoped there were corresponding Windows animation methods. I wonder if I could do low-rent window size animation by changing the frame iteratively, and forcing it to be redisplayed? Or maybe the MonkeyBread libraries have support for that kind of animation? (have to ask @Christian_Schmitz about that!)

When I have more than a couple of controls on a page of a page panel I use containers. That way everything is more OO and more organised. The controls are usually private so that the containers need to communicate between each other.

1 Like

@Beatrix_Willius – great suggestion!

We have NSWindowMBS class with method:

setFrame(frameRect as NSRectMBS, display as boolean, animated as boolean)

Sorry, I canā€˜t resist:

And that means what? This totally UI bullsh** and I hate it for decades. It has absolutely no value to the user and is simply gimmicky.

@Carsten_Belling – as a UI designer, I find it elegant and helpful. Preference/Option/Setting panels can have a lot of tabs, and it’s nice 1) see all that tab’s options at once without scrolling, and not to have to either 2) cram all your options into the same small space, or 3) have a tab with a few options and lots of empty white space.

1 Like

@Christian_Schmitz – so NOT on Windows – only MacOS?

Animation on Windows? Well, you’d better not do that.

1 Like

Because it’s flashy? The animation sucks? Even on a modern Windows 10/11 machine?

Because both the declare and the MBS plugin use a macOS only API. I think that might have got a little lost in translation.

OH – there is no MBS plugin for Windows? (otherwise, I’m not following what the limitation is)

Apple wrote a fancy animated window routine for developers to use. Microsoft did not.