Page 1 of 1

Setting a Child Window into Parent

Posted: Mon Nov 24, 2025 12:50 am
by SMaag
I want to set a ChildWindow into a ParentWindow and I want the ChildWindow to move only inside the Parent.

The ParentID Flag in Openwindow doesn't do this.
I have to use the Windows API SetParent() and after that it works!

The Questions:
1. Is there a PB only way to do this?
2. If not, how to do this on other OS Mac an Linux?

Here my sample code

Code: Select all


EnableExplicit
Define wndFlags
Define x,y,w,h

Enumeration Windows
  #wnd_Main 
  #wnd_Child1
  #wnd_Child2
  #wnd_Child2_Child
EndEnumeration

ExamineDesktops()
h = DesktopHeight(0) : w = DesktopWidth(0)
h= h/2
w = w/2
x=0 : y=0

wndFlags = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered
If OpenWindow(#wnd_Main, x, y, w, h, "MainWindow", wndFlags)
   
  x= WindowX(#wnd_Main, #PB_Window_InnerCoordinate)
  y= WindowY(#wnd_Main, #PB_Window_InnerCoordinate)
  w= WindowWidth(#wnd_Main, #PB_Window_InnerCoordinate)
  h= WindowHeight(#wnd_Main, #PB_Window_InnerCoordinate)

  wndFlags = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget
  
  If OpenWindow(#wnd_Child1,10,20,w/3,h/3, "Child Window 1", wndFlags, WindowID(#wnd_Main))
    Debug "ChildWindow"
    Debug "x = " + WindowX(#wnd_Child1)
    Debug "y = " + WindowY(#wnd_Child1)
    
    SetParent_(WindowID(#wnd_Child1), WindowID(#wnd_Main))  ; !!! Is there a PureBasic way without WinAPI
  EndIf
  
  x= WindowX(#wnd_Child1, #PB_Window_FrameCoordinate)
  y= WindowY(#wnd_Child1, #PB_Window_FrameCoordinate)
  w= WindowWidth(#wnd_Child1, #PB_Window_InnerCoordinate)
  h= WindowHeight(#wnd_Child1, #PB_Window_InnerCoordinate)
  
  If OpenWindow(#wnd_Child2,w+40,20,w,h, "Child Window 2", wndFlags, WindowID(#wnd_Main))
    Debug "ChildWindow"
    Debug "x = " + WindowX(#wnd_Child1)
    Debug "y = " + WindowY(#wnd_Child1)
    
    SetParent_(WindowID(#wnd_Child2), WindowID(#wnd_Main))
    
    x= WindowX(#wnd_Child2, #PB_Window_InnerCoordinate)
    y= WindowY(#wnd_Child2, #PB_Window_InnerCoordinate)
    w= WindowWidth(#wnd_Child2, #PB_Window_InnerCoordinate)
    h= WindowHeight(#wnd_Child2, #PB_Window_InnerCoordinate)
    
    If OpenWindow(#wnd_Child2_Child, 0, 0,w/2,h/2, "Child of Child 2", wndFlags, WindowID(#wnd_Child2))
      Debug "ChildWindow"
      Debug "x = " + WindowX(#wnd_Child2)
      Debug "y = " + WindowY(#wnd_Child2)
      
      SetParent_(WindowID(#wnd_Child2_Child), WindowID(#wnd_Child2))
    EndIf
    
  EndIf

EndIf

Define Event, Quit

Repeat
  Event = WaitWindowEvent()
  
  Select EventWindow()
    Case #wnd_Child1
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child1)
      EndIf
      
    Case #wnd_Child2
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child2)
      EndIf
      
    Case #wnd_Child2_Child
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child2_Child)
      EndIf
      
    Case #wnd_Main
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        Quit = #True
      EndIf
    
  EndSelect  
  
Until Quit = #True


Re: Setting a Child Window into Parent

Posted: Mon Nov 24, 2025 9:56 am
by jacdelad
The parent id is not intended for this purpose. Have you tried the MDIGadget? https://www.purebasic.com/german/docume ... adget.html

Re: Setting a Child Window into Parent

Posted: Mon Nov 24, 2025 10:33 am
by SMaag
The parent id is not intended for this purpose. Have you tried the MDIGadget?
I know the MDI Gadget and I know how to use it. But MDI is Windows only.

The TestCode shows how to do under Windows but with API SetParent().

The goal is a code for optional Window arrangement. Free floating Windows or nested Windows.

Should work under Linux and Mac too. Because of that a PB only solution or with API on Windows, Max, Linux

Re: Setting a Child Window into Parent

Posted: Mon Nov 24, 2025 11:39 am
by jacdelad
Ah sorry, I usually code with Windows, so I wasn't aware of this. Since you already found SetParent I cannot help you further.

Re: Setting a Child Window into Parent

Posted: Mon Nov 24, 2025 5:47 pm
by mk-soft
For maOS addChildWindow ...

Even if you can drag the child window outside the parent window, the child windows are moved by the parent windows when dragging.

Update v1.01.3
Unfortunately, you cannot change the order of the child windows.
To bring the active child window to the foreground, you must change the level.
But set it back to zero, otherwise the child window will remain in the foreground above all other apps.

Update v1.02.1
- For All OS
- It may be that MoveWindow does not work at Wayland (New security feature from Wayland)

Code: Select all

;-TOP by mk-soft, v1.01.1, 12.11.2025

; PB v6.21 (Debian 13) Fix C-Backend PB_FreeGtkBase over C-Macro
CompilerIf #PB_Compiler_OS = #PB_OS_Linux And #PB_Compiler_Version = 621
  CompilerIf #PB_Compiler_Thread And #PB_Compiler_Backend = #PB_Backend_C
    Procedure FixFreeGtkBaseThread()
      !#define PB_FreeGtkBase() PB_FreeGtkBase_THREAD()
    EndProcedure : FixFreeGtkBaseThread()
  CompilerEndIf
CompilerEndIf

;-TOP by mk-soft, v1.02.2

#ProgramTitle = "Main Window"
#ProgramVersion = "v1.02.2"

Enumeration Windows
  #Main
EndEnumeration

Enumeration MenuBar
  #MainMenu
EndEnumeration

Enumeration MenuItems
  #MainMenuAbout
  #MainMenuExit
EndEnumeration

Enumeration Gadgets
  
EndEnumeration

Enumeration StatusBar
  #MainStatusBar
EndEnumeration

#NSWindowAbove  = 1
#NSWindowBelow  = -1
#NSWindowOut    = 0

#ChildStyle = #PB_Window_SystemMenu; | #PB_Window_SizeGadget

ImportC ""
  gtk_window_set_attached_to(window, widget)
EndImport

Procedure UpdateWindow()
  Protected dx, dy
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
  ; Resize gadgets
EndProcedure

Procedure ChildWindow_1(Parent)
  Protected x, y, d, dx
  
  CompilerIf Not #PB_Compiler_OS = #PB_OS_Windows
    x = WindowX(Parent, #PB_Window_InnerCoordinate)
    y = WindowY(Parent, #PB_Window_InnerCoordinate)
  CompilerEndIf
  
  If OpenWindow(1, x+10, y+10, 400, 200, "Child Window 1", #ChildStyle, WindowID(Parent))
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_MacOS
        CocoaMessage(0, WindowID(Parent), "addChildWindow:", WindowID(1), "ordered:", #NSWindowAbove)
      CompilerCase #PB_OS_Linux
        gtk_window_set_transient_for_(WindowID(1), WindowID(0))
      CompilerCase #PB_OS_Windows
        SetParent_(WindowID(1), WindowID(Parent))
    CompilerEndSelect
  EndIf
EndProcedure

Procedure ChildWindow_2(Parent)
  Protected x, y, d, dx
  
  CompilerIf Not #PB_Compiler_OS = #PB_OS_Windows
    x = WindowX(Parent, #PB_Window_InnerCoordinate)
    y = WindowY(Parent, #PB_Window_InnerCoordinate)
  CompilerEndIf
  
  If OpenWindow(2, x+420, y+10, 400, 200, "Child Window 2", #ChildStyle, WindowID(Parent))
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_MacOS
        CocoaMessage(0, WindowID(Parent), "addChildWindow:", WindowID(2), "ordered:", #NSWindowAbove)
      CompilerCase #PB_OS_Linux
        gtk_window_set_transient_for_(WindowID(2), WindowID(0))
      CompilerCase #PB_OS_Windows
        SetParent_(WindowID(2), WindowID(Parent))
    CompilerEndSelect
  EndIf
EndProcedure

Procedure SetChildLevel(State)
  Static lastChild
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    If State
      Select EventWindow()
        Case 1
          lastChild = 1
        Case 2
          lastChild = 2
      EndSelect
      
      Select lastChild
        Case 1
          CocoaMessage(0, WindowID(1), "setLevel:", 1)
          CocoaMessage(0, WindowID(2), "setLevel:", 0)
          lastChild = 1
        Case 2
          CocoaMessage(0, WindowID(1), "setLevel:", 0)
          CocoaMessage(0, WindowID(2), "setLevel:", 1)
          lastChild = 2
      EndSelect
    Else  
      CocoaMessage(0, WindowID(1), "setLevel:", 0)
      CocoaMessage(0, WindowID(2), "setLevel:", 0)
    EndIf
  CompilerEndIf  
EndProcedure

Procedure MoveChildWindow()
  Static x = -1
  Static y = -1
  Static x2, y2, dx, dy
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Linux
    If x = -1
      x = WindowX(0)
      y = WindowY(0)
    Else
      x2 = WindowX(0)
      y2 = WindowY(0)
      dx = x2 - x
      dy = y2 -y
      x = x2
      y = y2
      ResizeWindow(1, WindowX(1) + dx, WindowY(1) + dy, #PB_Ignore, #PB_Ignore)
      ResizeWindow(2, WindowX(2) + dx, WindowY(2) + dy, #PB_Ignore, #PB_Ignore)
    EndIf
  CompilerEndIf
EndProcedure

Procedure Main()
  Protected dx, dy
  Protected lastChild
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 840, 600, #ProgramTitle , #MainStyle)
    ; Menu
    CreateMenu(#MainMenu, WindowID(#Main))
    MenuTitle("&File")
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      MenuItem(#PB_Menu_About, "")
    CompilerElse
      MenuItem(#MainMenuAbout, "About")
    CompilerEndIf
    ; Menu File Items
    
    CompilerIf Not #PB_Compiler_OS = #PB_OS_MacOS
      MenuBar()
      MenuItem(#MainMenuExit, "E&xit")
    CompilerEndIf
    
    ; StatusBar
    CreateStatusBar(#MainStatusBar, WindowID(#Main))
    AddStatusBarField(#PB_Ignore)
    
    ; Gadgets
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
    ; Bind Events
    BindEvent(#PB_Event_SizeWindow, @UpdateWindow(), #Main)
    
    While WaitWindowEvent(100) : Wend
    
    ChildWindow_1(0)
    ChildWindow_2(0)
    
    SetActiveWindow(0)
    
    BindEvent(#PB_Event_MoveWindow, @MoveChildWindow(), 0)
    
    ; Event Loop
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              Break
              
          EndSelect
          
        Case #PB_Event_ActivateWindow
          SetChildLevel(#True)
          
        Case #PB_Event_DeactivateWindow
          SetChildLevel(#False)
          
        Case #PB_Event_MoveWindow
          Select EventWindow()
            Case 0
              
          EndSelect
          
        Case #PB_Event_Menu
          Select EventMenu()
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                PostEvent(#PB_Event_Menu, #Main, #MainMenuAbout)
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                PostEvent(#PB_Event_CloseWindow, #Main, #Null)
                
            CompilerEndIf
            
          Case #MainMenuAbout
            MessageRequester("About", #ProgramTitle + #LF$ + #ProgramVersion, #PB_MessageRequester_Info)
              
          Case #MainMenuExit
            PostEvent(#PB_Event_CloseWindow, #Main, #Null)
            
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
          EndSelect
          
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()

Re: Setting a Child Window into Parent

Posted: Tue Nov 25, 2025 6:39 pm
by SMaag
Tanks a lot!

gtk_window_set_transient_for_(WindowID(2), WindowID(0))

I tried it under OpenSuse KDE with purebasic_qt. It works! Step by step I start to understand the Linux better.
What I do not understand is, why we have a Qt version of Purebasic.

But I think, I'm generally on the wrong way. Window in Window isn't a suitable way on Linux.
I checked some Linux Software. It always opens a complete new instance of the program or use tabs.
IrfanView what is cross platform can open only 1 Image per instance.

On Windows MDI is the better solution.

Re: Setting a Child Window into Parent

Posted: Tue Nov 25, 2025 6:53 pm
by minimy
Hi SMaag, May be som thing like this?

Code: Select all

  #Main = 0
  #MDIChild = 1
  If OpenWindow(#Main, 0, 0, 400, 300, "MDIGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget)
    If CreateMenu(#Main, WindowID(#Main))
      MenuTitle("Menu index 0")
      MenuTitle("MDI windows menu")
        MenuItem(0, "self created item")
        MenuItem(1, "self created item")

      MDIGadget(0, 0, 0, 0, 0, 1, 2, #PB_MDI_AutoSize)
        AddGadgetItem(0, #MDIChild, "child window")
          ; add gadgets here...
      UseGadgetList(WindowID(#Main)) ; go back to the main window gadgetlist
    EndIf
    Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
  EndIf


Re: Setting a Child Window into Parent

Posted: Tue Nov 25, 2025 10:46 pm
by mk-soft
Even if you start the PureBasic QT IDE, a gtk3 version is compiled.

Why QT
Maybe for a system where there is no gtk. (What I don't believe ;) )

Re: Setting a Child Window into Parent

Posted: Wed Nov 26, 2025 12:36 am
by BarryG
jacdelad wrote: Mon Nov 24, 2025 9:56 amThe parent id is not intended for this purpose.
What is it actually for? I've never seen a description posted for what its practical use is.

Re: Setting a Child Window into Parent

Posted: Wed Nov 26, 2025 1:49 am
by RASHAD
PB ParentID is like Windows API #GWL_HWNDPARENT
It only keeps the child window over the parent window not as exact child
You can try restrict the child window moving
Next is just to show what I mean

Code: Select all


;EnableExplicit
Define wndFlags
Define x,y,w,h

Enumeration Windows
  #wnd_Main 
  #wnd_Child1
  #wnd_Child2
  #wnd_Child2_Child
EndEnumeration

Procedure moveCB()
  If WindowX(#wnd_Child1) < WindowX(#wnd_Main)
    ResizeWindow(#wnd_Child1,WindowX(#wnd_Main),#PB_Ignore,#PB_Ignore,#PB_Ignore)
  EndIf
  If WindowY(#wnd_Child1) < WindowY(#wnd_Main)
    ResizeWindow(#wnd_Child1,#PB_Ignore,WindowY(#wnd_Main),#PB_Ignore,#PB_Ignore)
  EndIf
  If (WindowX(#wnd_Child1) + WindowWidth(#wnd_Child1) + 10) > (WindowWidth(#wnd_Main)+WindowX(#wnd_Main))
    ResizeWindow(#wnd_Child1,WindowX(#wnd_Child1)-10,#PB_Ignore,#PB_Ignore,#PB_Ignore)
  EndIf
;   If (WindowY(#wnd_Child1) + WindowHeight(#wnd_Child1)) > WindowHeight(#wnd_Main)
;     ResizeWindow(#wnd_Child1,#PB_Ignore,WindowY(#wnd_Child1) + WindowHeight(#wnd_Child1),#PB_Ignore,#PB_Ignore)
;   EndIf
EndProcedure 

ExamineDesktops()
h = DesktopHeight(0) : w = DesktopWidth(0)
h= h/2
w = w/2
x=0 : y=0

wndFlags = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered
If OpenWindow(#wnd_Main, x, y, w, h, "MainWindow", wndFlags)
  StickyWindow( #wnd_Main,1)
  x= WindowX(#wnd_Main, #PB_Window_InnerCoordinate)
  y= WindowY(#wnd_Main, #PB_Window_InnerCoordinate)
  w= WindowWidth(#wnd_Main, #PB_Window_InnerCoordinate)
  h= WindowHeight(#wnd_Main, #PB_Window_InnerCoordinate)

  wndFlags = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget
  
  If OpenWindow(#wnd_Child1,WindowX(#wnd_Main)+10,WindowY(#wnd_Main)+20,w/3,h/3, "Child Window 1", PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered, WindowID(#wnd_Main))
;     Debug "ChildWindow"
;     Debug "x = " + WindowX(#wnd_Child1)
;     Debug "y = " + WindowY(#wnd_Child1)
    
  EndIf
  
  x= WindowX(#wnd_Child1, #PB_Window_FrameCoordinate)
  y= WindowY(#wnd_Child1, #PB_Window_FrameCoordinate)
  w= WindowWidth(#wnd_Child1, #PB_Window_InnerCoordinate)
  h= WindowHeight(#wnd_Child1, #PB_Window_InnerCoordinate)
  
  BindEvent(#PB_Event_MoveWindow,@moveCB(),#wnd_Child1)
  
;   If OpenWindow(#wnd_Child2,w+40,20,w,h, "Child Window 2", wndFlags, WindowID(#wnd_Main))
;     Debug "ChildWindow"
;     Debug "x = " + WindowX(#wnd_Child1)
;     Debug "y = " + WindowY(#wnd_Child1)    
;    
;     x= WindowX(#wnd_Child2, #PB_Window_InnerCoordinate)
;     y= WindowY(#wnd_Child2, #PB_Window_InnerCoordinate)
;     w= WindowWidth(#wnd_Child2, #PB_Window_InnerCoordinate)
;     h= WindowHeight(#wnd_Child2, #PB_Window_InnerCoordinate)
;     
;     If OpenWindow(#wnd_Child2_Child, 0, 0,w/2,h/2, "Child of Child 2", wndFlags, WindowID(#wnd_Child2))
;       Debug "ChildWindow"
;       Debug "x = " + WindowX(#wnd_Child2)
;       Debug "y = " + WindowY(#wnd_Child2)
;       
;     EndIf
;     
;   EndIf

EndIf

Define Event, Quit

Repeat
  Event = WaitWindowEvent()
  
  Select EventWindow()
    Case #wnd_Child1
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child1)
      EndIf
      
    Case #wnd_Child2
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child2)
      EndIf
      
    Case #wnd_Child2_Child
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        CloseWindow(#wnd_Child2_Child)
      EndIf
      
    Case #wnd_Main
      If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
        Quit = #True
      EndIf
    
  EndSelect  
  
Until Quit = #True