How to properly use parts of HGui in a hollywood program?

Find quick help here to get you started with Hollywood
Post Reply
amyren
Posts: 446
Joined: Thu May 02, 2019 11:53 am

How to properly use parts of HGui in a hollywood program?

Post by amyren »

I am trying to use HGui to create a edit textbox within my program.

I can get it to work, but it will only work once per session.
What happens is that I start the program with a regular hollywood display, then I open the textbox and can edit the text as I need, then save and close the textbox and return back to the program.

Well, almost as easy as that.
When HGui open the textbox, it seem to take over my display 1. In order to return to my program I must first create a temporary second display, then I can close display 1 in order to re-create it and open it. That was the only method I found that made me able to reopen display 1.
Perhaps I have over-complicated this, and there is a more natural way to do it?

At this point the textbox is gone and I have the same display as when I started. But if trying to open the textbox again, there will be an error.

Code: Select all

Error in line 4113 (HGui.hws): Table newwin{} not found!
It seems that I have not managed to get rid of the remains of the textbox window.

I tried to free or close the HGui textbox window by using one of these lines, by using either will just kill the whole program.

Code: Select all

Gadget.Result.Window:free()
Gadget.Result.Window:close()
I made an example script to demonstrate the issue

Code: Select all

@DISPLAY {Title = "HGUI test", Width = 800, Height = 600}

@INCLUDE "C:/Program Files/Hollywood/HGui-main/+Includes.hws"
@INCLUDE #INC_HGUI

BeginDoubleBuffer()
SetFillStyle(#FILLCOLOR)

Function p_displaysomething()
	xpos = Rnd(750)+1
	ypos = Rnd(580)+1
	xsize = Rnd(400)+1
	ysize = Rnd(300)+1

	Box(xpos, ypos, xsize, ysize, GetRandomColor())
	SetFont(#SERIF, 24)
	TextOut(550, 20, "CLICK HERE TO TEST")
	Flip	
EndFunction

Function p_HandlerFunc(msg)
	Switch(msg.Action)
	Case "OnMouseDown":
		xpos = MouseX()
		ypos = MouseY()
		If xpos > 600 And ypos < 100
			p_inputbox
		EndIf
	Case "CloseWindow":
		Wait(50)
		End
	EndSwitch
EndFunction		

Function p_inputbox()
	EndDoubleBuffer()	
	ClearInterval(1)
	
	If Exists("test.txt")
		OpenFile(1, "test.txt")
		While Not Eof(1) 
			string$ = string$.."\n"..ReadLine(1)
		Wend
		CloseFile(1)
		string$ = StripStr(string$)
	Else
		string$ = "Edit this"
	EndIf
	
	myWin = HGui.Window:InputBoxNew(
		{ text = string$,
		ok = "Ok",
		cancel = { "Cancel" },
		callbackOk = p_callback,
		callbackCancel = p_callback,
		;callbackClose = p_callback,
		;--- some standard window's tags ---
		size = { w = 600, h = 300 },
		title = "The HGui Input Box...",
		name = "inputBox1"
		})

	If HGui.windowExists("inputBox1")
		myWin:Set({ title = "Textbox"}, True)
	EndIf
EndFunction 

Function p_callback(Gadget)
	If gadget.Result.Cancel
		SystemRequest("Cancelled", "You pressed Cancel!", "OK")
	Else
		SystemRequest("Text saved to file", "Saved to file:\n"..Gadget.Result.text, "OK")
		StringToFile(Gadget.Result.text, "test.txt")
	EndIf
	
	
	;create a temporay display to be able to close and recreate display 1
	CreateDisplay(2, {width = 200, height = 100, HideTitleBar = True, Borderless = True})
	OpenDisplay(2)
	CloseDisplay(1)

	CreateDisplay(1, {Title = "HGUI second test", Width = 800, Height = 600})
	OpenDisplay(1)
	CloseDisplay(2)
	BeginDoubleBuffer
	Flip
	Box(0, 0, 1366, 768, #SILVER); Background
	Flip
	SetInterval(1, p_displaysomething, 100)
	InstallEventHandler({ OnMouseDown = p_HandlerFunc, CloseWindow = p_HandlerFunc})

;using either of these commands will kill the program:
;Gadget.Result.Window:free()
;Gadget.Result.Window:close()	

EndFunction

InstallEventHandler({ OnMouseDown = p_HandlerFunc, OnCloseWindow = p_HandlerFunc })

SetInterval(1, p_displaysomething, 100)

Repeat
	WaitEvent()
Forever
User avatar
Allanon
Posts: 746
Joined: Sun Feb 14, 2010 7:53 pm
Location: Italy
Contact:

Re: How to properly use parts of HGui in a hollywood program?

Post by Allanon »

Hello!
I've seen your code and here are some points you have to consider:
- HGui was build to create app with a GUIs using an hierarchical structure where everything starts from a root display, what you are doing is a bit of an hack but I'll help you to figure out how to use what you need :)
- The :InputBoxNew() is an helper function that is used to quickly build a window on a living HGui app already defined, that's why using :Free() or :Close() methods it quits you app, HGui sees that the :InputBox() is the last opened window and terminate the application.
To avoid this problem there is a function used to switch this behaviour:
HGui.SetQuitOnLastWindow(value) -> Replace value with False and you app will not be terminated.
But there is another problem: for HGui the InputBox window is the first display you opened (because it counts HGui windows) and the problem is in the HGui.Window:New(), precisely in this code fragment:

Code: Select all

  ;===[ BUILD PROCESS ]========================================================
  ; Check if we are building the first window
  Local first_window = IIf(TB.Count(HGui.Windows) = 0, True, False)

  If first_window ;::::::::::::::::::::::::::::::::::::::::::: FIRST WINDOW :::
    NewWin.private.HWId = 1
    ...
That explains why your display 1 is destroyed and used by the InputBox().

---
Now let's see how you can use the InputBox without going mad :)
---
Solution (I think it's the best if your app can tolerate windows)
1) The @DISPLAY command is used to initialize the display but it will be changed by HGui because it needs to initialize its stuff:

Code: Select all

main_window = HGui.Window:new(
  { title    = "TEST",
    name     = "main",
    position = { x = #CENTER, y = #CENTER },
    size     = { w = 800, h = 600 },
    
    ; Let's attach a confirmation requester when the user
    ; closes the main window clicking on the close gadget.
    flags    = { AutoClose = False },
    events   = { OnClose = Function(msg)
                             Local answer = SystemRequest("ARE YOU SURE?", 
                                                          "Do you want to quit?",
                                                          "Yes|No")
                             If answer = 1 Then End
                           EndFunction
                 }
    })
Since this is the first display it is the number 1, always.
2) Using the above code the InputBox will not destroy anymore your display, so there is no need to use the open/close display trick in p_callback(Gadget) function which becames:

Code: Select all

Function p_callback(Gadget)
	If gadget.Result.Cancel
		SystemRequest("Cancelled", "You pressed Cancel!", "OK")
	Else
		SystemRequest("Text saved to file", "Saved to file:\n"..Gadget.Result.text, "OK")
		StringToFile(Gadget.Result.text, "test.txt")
	EndIf
	
  Gadget.Result.Window:free()

EndFunction
3) InputBox() is a non-blocking method, this means that in this specific example you don't need to pause your current intervals or disable DoubleBuffer(), you just need to modify the p_displaySomething() adding a SelectDisplay():

Code: Select all

Function p_displaysomething()
  SelectDisplay( 1, True)
	xpos = Rnd(750)+1
	ypos = Rnd(580)+1
	xsize = Rnd(400)+1
	ysize = Rnd(300)+1

	Box(xpos, ypos, xsize, ysize, GetRandomColor())
	SetFont(#SERIF, 24)
	TextOut(550, 20, "CLICK HERE TO TEST")
	Flip	
EndFunction
Last thing: if you need a scroll bar for your text you can duplicate the :InputBox() method and create a new one that supports a scrollbar, drop me a line if you need help with that.

Your final program is not much different from your original one:

Code: Select all

@DISPLAY {Title = "HGUI test", Width = 800, Height = 600}

@INCLUDE "+Includes.hws"
@INCLUDE #INC_HGUI

BeginDoubleBuffer()
SetFillStyle(#FILLCOLOR)

main_window = HGui.Window:new(
  { title    = "TEST",
    name     = "main",
    position = { x = #CENTER, y = #CENTER },
    size     = { w = 800, h = 600 },
    bgcolor = #RED,
    ; Let's attach a confirmation requester when the user
    ; closes the main window clicking on the close gadget.
    flags    = { AutoClose = False },
    events   = { OnClose = Function(msg)
                             Local answer = SystemRequest("ARE YOU SURE?", 
                                                          "Do you want to quit?",
                                                          "Yes|No")
                             If answer = 1 Then End
                           EndFunction
                 }
    })


Function p_displaysomething()
  SelectDisplay( 1, True)
	xpos = Rnd(750)+1
	ypos = Rnd(580)+1
	xsize = Rnd(400)+1
	ysize = Rnd(300)+1

	Box(xpos, ypos, xsize, ysize, GetRandomColor())
	SetFont(#SERIF, 24)
	TextOut(550, 20, "CLICK HERE TO TEST")
	Flip
EndFunction

Function p_HandlerFunc(msg)
	Switch(msg.Action)
	Case "OnMouseDown":
		xpos = MouseX()
		ypos = MouseY()
		If xpos > 600 And ypos < 100
			p_inputbox
		EndIf
	Case "CloseWindow":
		Wait(50)
		End
	EndSwitch
EndFunction		

Function p_inputbox()
;	EndDoubleBuffer()	
;	ClearInterval(1)
	
	If Exists("test.txt")
		OpenFile(1, "test.txt")
		While Not Eof(1) 
			string$ = string$.."\n"..ReadLine(1)
		Wend
		CloseFile(1)
		string$ = StripStr(string$)
	Else
		string$ = "Edit this"
	EndIf
	
  HGui.SetQuitOnLastWindow(False)

	myWin = HGui.Window:InputBoxNew(
		{ text = string$,
		ok = "Ok",
		cancel = { "Cancel" },
		callbackOk = p_callback,
		callbackCancel = p_callback,
		;callbackClose = p_callback,
		;--- some standard window's tags ---
		size = { w = 600, h = 300 },
		title = "The HGui Input Box...",
		name = "inputBox1"
		})

	If HGui.windowExists("inputBox1")
		myWin:Set({ title = "Textbox"}, True)
	EndIf
EndFunction 

Function p_callback(Gadget)
	If gadget.Result.Cancel
		SystemRequest("Cancelled", "You pressed Cancel!", "OK")
	Else
		SystemRequest("Text saved to file", "Saved to file:\n"..Gadget.Result.text, "OK")
		StringToFile(Gadget.Result.text, "test.txt")
	EndIf
	
  Gadget.Result.Window:free()

EndFunction

main_window:render()

InstallEventHandler({ OnMouseDown = p_HandlerFunc, OnCloseWindow = p_HandlerFunc })

SetInterval(1, p_displaysomething, 100)

Repeat
	WaitEvent()
Forever
---
Now that you use HGui to initialize you display have a look at all the things it has for you, you may be get rid of some of you event handlers and use the inbuilt ones, here is what the HGui window has to offer:

Code: Select all

/*---------------------------------------------------------
winObject = HGui.Window:new(options)

Create a new HGui window.
-----------------------------------------------------------
INPUT
  options : A table used to override default values.
    title : Window's title.
    name : Unique window's name, if you omit this argument an unique name will be assigned to the window for you.
    vsync : True|False
    position : Window's position in the host screen.
      x : Horizontal position (pixels). Defaults to #CENTER.
      y : Vertical position (pixels). Defaults to #CENTER.
    size : Window's size
     w : Width in pixels. Defaults to 320.
     h : Height in pixels. Defaults to 200.
    sizemax : Maximum size the window can be resized to. Defaults to the host screen size.
      w : Width in pixels. Defaults to 320.
      h : Height in pixels. Defaults to 200.
    sizemin : Minimum size the window can be resize to. Defaults to <size>.
      w : Width in pixels. Defaults to 320.
      h : Height in pixels. Defaults to 200.
    cacheroot : Cache root gadget (True|False)
    bgcolor : Background color
    background : Image file name used to fill the background
    bgtexture : Image file used as tile to fill the background
    bggradient : Table used to define a multi-color gradient to fill the background
      type : Gradient type : #LINEAR, #RADIAL, #CONICAL
      angle : Gradient angle in degrees
      colors : Gradient colors definition, it's atable composed by pairs [color]-[position]. Color is an RBG color value and position is a number in the 0-1 range. Note that the first color must have the position 0   and the last one the position 1.
    hotkeys : A table with window's action hotkeys
      hide : A key associated to the window:hide() method
      show : A key associated to the window:show() method
      activate : A key associated to the window:activate() method
      close : A key associated to the window:close() method
      open : A key associated to the window:open() method
    customhotkeys : A table with custom hotkeys you can assign with your own function, each definition is a subtable with the following items:
      qualifier : Key qualifier (LCONTROL, RCONTROL, etc...)
      key : Assigned key
      event : Event (OnKeyDown, ...)
      callback : Callback function
    flags : Window's flags
      resizeable : Resizeable window? (True|False)
      closeable : Closeable window? (True|False)
      moveable : Moveable window? (True|False)
      borderless : Borderless window? (True|False)
      modal : Modal window? (True|False)
      hidefromtaskbar : Hide from the taskbar (True|False)
      hideable : Hideable window? (True|False)
      autoclose : <False> to receive the event but to keep the window opened, you have to close it in your  callback function.
    events : Callback functions table
      onclose : Called when the window is closed (passed params: eventMessage, windowObject)
      onmove : Called when the window is move
      onactivate : Called when the window get the focus
      ondeactivate : Called when the window lost the focus
      onhide : Called when the window is hided
      onshow : Called when the window is showed
      onsize : Called when the window is resized
      ondrop : Called when a file is dropped into the window
    dontopen : TRUE to create the window without opening
    contextual : Contextual menu (same structure as .menu field) 
    menu : Top menu definition, please have a look at :menuSet() method for detailed informations.

OUTPUT
  winObject : The window object created or FALSE if an error has occured.
---------------------------------------------------------*/
The alternative to this is to dynamically create the TextBox in the first display, but I think that the above method is simpler and requires less code modification.

Sorry for the very long post :)
Let me know if it's all fine!

Fabio
----------------------------
[Allanon] Fabio Falcucci | GitHub (leaving) | Gitea (my new house) | My Patreon page | All my links
amyren
Posts: 446
Joined: Thu May 02, 2019 11:53 am

Re: How to properly use parts of HGui in a hollywood program?

Post by amyren »

Thank you for providing a solution as well as explaining things.
That is very helpful of you.

Your suggestion for the final program run well, although a minor adjustment had to be done. The BeginDoubleBuffer() line must be executed after the main_window section or it will throw an error.
And I also realize my string$ variable must be cleared before reading my file again in the inputbox function or else the content will double itself every time. Thats on me, I only noticed this since now is the first time I have been able to execute the inputbox twice in a session:)

The usage of DoubleBuffer also complicates things. For example if resizing the window then it will create an error.
For the example provided the usage of doublebuffer is really not important, but in my other program I wil notice the flickering more if trying without it.
I notice there is a onsize event, I could probably use that as a callback and disable DoubleBuffer temporary while resizing.

Another thing, in my program I did use the scaling engine for the display so all graphichs would scale properly when maximizing the display. Is this possible with the HGui window?
User avatar
Allanon
Posts: 746
Joined: Sun Feb 14, 2010 7:53 pm
Location: Italy
Contact:

Re: How to properly use parts of HGui in a hollywood program?

Post by Allanon »

Hello!
Implementing support for scaling methods has not been done, but you can try the following, I've changed the bottom part of the script like this (I've removed the BeginDoubleBuffer() from the top of the script):

Code: Select all

main_window:render()

;--- ADDED ---
main_window:select()
SetDisplayAttributes({ ScaleMode = #SCALEMODE_AUTO})
BeginDoubleBuffer()
;-------------
InstallEventHandler({ OnMouseDown = p_HandlerFunc, OnCloseWindow = p_HandlerFunc })

SetInterval(1, p_displaysomething, 100)

Repeat
	WaitEvent()
Forever
The bad news is that I've tried but the InputBox coordinates are screwed up and I cannot click the buttons if the parent window changes its size.
It seems that the coordinates reported by the mouse events are relative to the first display even if they are coming from the input box display which has the scaling method set to #SCALEMODE_NONE.
It may be a wrong behavior on my setup, I'm on Linux Manjaro + Hollywood 10.
Let me know if it works for you :)
----------------------------
[Allanon] Fabio Falcucci | GitHub (leaving) | Gitea (my new house) | My Patreon page | All my links
amyren
Posts: 446
Joined: Thu May 02, 2019 11:53 am

Re: How to properly use parts of HGui in a hollywood program?

Post by amyren »

Excellent.
Errors when resizing the window are now gone.

About the mouse coordinates I think this is deliberately made like this in Hollywood. Eg. if my program is made with a 800 x 600 window in mind and I place an object at near the bottom right of the screen at position X = 700 and Y = 550 and I have set an action if the mouse is pressed within the coordinates of that object. Then if I maximize and scale this window to fit a 1920x1080 screen, then mouse position still will be reported as 700 x 550 when I hit the object even if the real screen coordinates for the mouse position are near 1680 x 990.

Anyway, I did not notice any misbehaviour in this matter so far when maximizing my window here.

Thanks again.
User avatar
Allanon
Posts: 746
Joined: Sun Feb 14, 2010 7:53 pm
Location: Italy
Contact:

Re: How to properly use parts of HGui in a hollywood program?

Post by Allanon »

No problem, I'm really happy that it works :)
----------------------------
[Allanon] Fabio Falcucci | GitHub (leaving) | Gitea (my new house) | My Patreon page | All my links
Post Reply