Additional flag for MessageRequester to center requester on app window

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
Axeman
User
User
Posts: 91
Joined: Mon Nov 03, 2003 5:34 am

Additional flag for MessageRequester to center requester on app window

Post by Axeman »

Hi, I'd like to request an additional flag for the MessageRequester command that tells it to center the requester window on the calling app's window. Similar to what the #PB_Window_WindowCentered flag does with OpenWindow.

I'll probably make a custom requester with this capability for my projects, as the #PB_Gadget_RequiredSize flag for GadgetHeight seems to return useful values for multi-line text gadgets now ( https://www.purebasic.fr/english/viewtopic.php?t=65761 ). This is a request for the standard MessageRequester command (adding this before anyone tells me to just make my own requester).
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Additional flag for MessageRequester to center requester on app window

Post by BarryG »

On Windows, the MessageRequester() just calls MessageBox_() API behind the scenes, and a center flag isn't supported by that, so your request can never be done.

But see here -> https://www.purebasic.fr/english/viewtopic.php?t=6680

And there's other posts with code if you search for "requester position".
Axeman
User
User
Posts: 91
Joined: Mon Nov 03, 2003 5:34 am

Re: Additional flag for MessageRequester to center requester on app window

Post by Axeman »

Fair point. I'll probably just start using a custom requester, as that gives me more control.
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Additional flag for MessageRequester to center requester on app window

Post by chi »

Axeman wrote: Sun Dec 12, 2021 7:17 am Fair point. I'll probably just start using a custom requester, as that gives me more control.
A custom requester with all available flags is probably more work than this...

Code: Select all

#PB_MessageRequester_WinCenter = 128

Procedure MonitorEnumProc(hMonitor, hdcMonitor, *lprcMonitor.RECT, dwData)
  Protected rgn = CreateRectRgnIndirect_(*lprcMonitor)
  CombineRgn_(dwData, dwData, rgn, #RGN_OR)
  DeleteObject_(rgn)
  ProcedureReturn #True
EndProcedure

Procedure CBTProc(nCode, wParam, lParam)
  If nCode < 0 : Goto rethook : EndIf
  If nCode = #HCBT_CREATEWND
    Protected *cw.CBT_CREATEWND = lParam
    Protected *cs.CREATESTRUCT = *cw\lpcs
    If *cs\lpszClass = 32770
      Protected pRect.RECT, rgn, x, y
      rgn = CreateRectRgn_(0, 0, 0, 0)
      EnumDisplayMonitors_(0, 0, @MonitorEnumProc(), rgn)
      GetWindowRect_(*cs\hWndParent, pRect)
      x = pRect\left + (pRect\right - pRect\left - *cs\cx)/2
      y = pRect\top  + (pRect\bottom - pRect\top - *cs\cy)/2
      If PtInRegion_(rgn, x, y) And PtInRegion_(rgn, x + *cs\cx, y) And PtInRegion_(rgn, x, y + *cs\cy) And PtInRegion_(rgn, x + *cs\cx, y + *cs\cy)
        *cs\x = x
        *cs\y = y
      EndIf
      DeleteObject_(rgn)
    EndIf
  EndIf
  rethook:
  ProcedureReturn CallNextHookEx_(0, nCode, wParam, lParam)
EndProcedure

Procedure _MessageRequester(title$, text$, flags=0)
  If flags & #PB_MessageRequester_WinCenter
    Protected hook = SetWindowsHookEx_(#WH_CBT, @CBTProc(), 0, GetCurrentThreadId_())
  EndIf
  Protected ret = MessageRequester(title$, text$, flags&~#PB_MessageRequester_WinCenter)
  If hook : UnhookWindowsHookEx_(hook) : EndIf
  ProcedureReturn ret
EndProcedure

Macro MessageRequester(title, text, flags)
  _MessageRequester(title, text, flags)
EndMacro

; ------------------------------------------------------------------------------------

CompilerIf #PB_Compiler_IsMainFile
  
  OpenWindow(0, 0, 0, 640, 400, "MessageRequester_WinCenter", #PB_Window_SystemMenu|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_ScreenCentered|#PB_Window_Invisible)
  ButtonGadget(0, 10, 10, 100, 30, "Show")
  HideWindow(0, #False)
  
  Repeat
    Define event = WaitWindowEvent()
    Select event
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 0
            MessageRequester("Info", ~"This MessageRequester is always CENTERED on the parent window,\nbut only if the client area of the MR is fully visible on the desktop(s).", #PB_MessageRequester_Info|#PB_MessageRequester_WinCenter)
        EndSelect
    EndSelect
  Until event = #PB_Event_CloseWindow
  
CompilerEndIf
Image
Last edited by chi on Wed Dec 15, 2021 1:42 am, edited 2 times in total.
Et cetera is my worst enemy
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Additional flag for MessageRequester to center requester on app window

Post by BarryG »

Nice, Chi! Just what the doctor ordered. Thanks!
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Additional flag for MessageRequester to center requester on app window

Post by chi »

BarryG wrote: Sun Dec 12, 2021 8:58 am Nice, Chi! Just what the doctor ordered. Thanks!
Hehe np. I have changed the code slightly to account for offscreen MessageRequesters.
Et cetera is my worst enemy
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: Additional flag for MessageRequester to center requester on app window

Post by AZJIO »

I calculated the place of appearance of the child window in such a way that it was not centered, but as close as possible to the edge for which the parent window was shifted.
Axolotl
Addict
Addict
Posts: 802
Joined: Wed Dec 31, 2008 3:36 pm

Re: Additional flag for MessageRequester to center requester on app window

Post by Axolotl »

+1
I created a similar solution to chi's for my own apps. Especially on an 4k monitor it is really enoying if your tool shows at an edge and the message requestor appears screen centered.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Additional flag for MessageRequester to center requester on app window

Post by chi »

I have adjusted the code to consider all 4 corner points (instead of just TL & RB). Now it works regardless of the multi-screen setup.
Et cetera is my worst enemy
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Additional flag for MessageRequester to center requester on app window

Post by BarryG »

Axolotl wrote: Mon Dec 13, 2021 4:53 pmEspecially on an 4k monitor it is really enoying if your tool shows at an edge and the message requestor appears screen centered.
So many apps do that! Very annoying and unprofessional.

@Chi: Why the check for "If nCode<0" and Goto? Because if nCode<>#HCBT_CREATEWND (3) then it's going to skip the If/EndIf block anyway.

Code: Select all

If nCode < 0 : Goto rethook : EndIf
If nCode = #HCBT_CREATEWND
  ; Blah
EndIf
rethook:
ProcedureReturn CallNextHookEx_(0, nCode, wParam, lParam)
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Additional flag for MessageRequester to center requester on app window

Post by chi »

BarryG wrote: Wed Dec 15, 2021 3:37 am @Chi: Why the check for "If nCode<0" and Goto? Because if nCode<>#HCBT_CREATEWND (3) then it's going to skip the If/EndIf block anyway.
From CBTProc callback function:
If nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx.
I think it is somewhat time sensitive, especially when more nCode checks are involved. I used a DLL without it for many years and never had any problems tho. It's up to you, but better safe than sorry ;)
Et cetera is my worst enemy
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: Additional flag for MessageRequester to center requester on app window

Post by AZJIO »

Code: Select all

EnableExplicit

Enumeration Window
	#Window_Main
	#Window_Child
EndEnumeration

Enumeration Gadget
	#a1
	#b1
	#a2
	#b2
	#yzit
EndEnumeration

Enumeration Gadget
	#ok
	#txt
	#chb
EndEnumeration

Structure MXYWH
	x.l
	y.l
	w.l
	h.l
	m.l
EndStructure

Global WinRect.MXYWH
Global WWE

Declare _MsgBox(txt$, w = 270, h = 180, t = 0)
Declare _ChildCoor(nWin, w, h, c=0, d=0)

If OpenWindow(#Window_Main, 0, 0, 420, 250, "Example...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ButtonGadget (#a1, 5, 5, 50, 30, "corner")
	ButtonGadget (#b1, 5, 45, 50, 30, "center")
	
	ButtonGadget (#a2, 360, 170, 50, 30, "corner")
	ButtonGadget (#b2, 360, 210, 50, 30, "center")
	
	ButtonGadget (#yzit, 170, 110, 70, 30, "reduce")
	
	Repeat
		WWE = WaitWindowEvent()
		
		Select EventWindow()
			Case #Window_Main
				Select WWE
					Case #PB_Event_Gadget
						Select EventGadget()
							Case #a1, #a2
								_MsgBox("Close the program window?", 0, 0, 1)
							Case #b1, #b2
								_MsgBox("Close the program window?")
							Case #yzit
								ResizeWindow(#Window_Main, #PB_Ignore, #PB_Ignore, 200, 70)
								ResizeGadget(#a1, 5, 3, #PB_Ignore, #PB_Ignore)
								ResizeGadget(#b1, 5, 35, #PB_Ignore, #PB_Ignore)
								ResizeGadget(#a2, 140, 3, #PB_Ignore, #PB_Ignore)
								ResizeGadget(#b2, 140, 35, #PB_Ignore, #PB_Ignore)
								ResizeGadget(#yzit, 60, 30, #PB_Ignore, #PB_Ignore)
						EndSelect
					Case #PB_Event_CloseWindow
						CloseWindow(#Window_Main)
						End
				EndSelect
				
			Case #Window_Child
				Select WWE
					Case #PB_Event_Gadget
						Select EventGadget()
							Case #ok
								CloseWindow(#Window_Child)
								DisableWindow(#Window_Main, #False)
						EndSelect
					Case #PB_Event_CloseWindow
						CloseWindow(#Window_Child)
						DisableWindow(#Window_Main, #False)
				EndSelect
		EndSelect
	ForEver
EndIf

Procedure _MsgBox(txt$, w = 270, h = 180, t = 0)
	If Not w
		w = 270
	EndIf
	If Not h
		h = 180
	EndIf
	_ChildCoor(#Window_Main, w, h, t, 30)
	With WinRect
		OpenWindow(#Window_Child, \x, \y, \w, \h, "Message", #PB_Window_SystemMenu)
	EndWith
	SetWindowLongPtr_(WindowID(#Window_Child), #GWL_HWNDPARENT, WindowID(#Window_Main))
	TextGadget(#txt, 10, 10, 250, 60, txt$)
	CheckBoxGadget(#chb, 20, h-80, 240, 20, "Don't ask again")
	ButtonGadget (#ok, (w-50)/2, h - 40, 50, 30, "Ок")
	DisableWindow(#Window_Main, #True)
EndProcedure

Procedure _ChildCoor(nWin, w, h, c=0, d=0)
	Protected DX, DY
	With WinRect
		\h = WindowHeight(nWin)
		\w = WindowWidth(nWin)
		\x = WindowX(nWin)
		\y = WindowY(nWin)
		
		ExamineDesktops()
		DX=DesktopWidth(0)
		DY=DesktopHeight(0)
		If c = 0
			\x+(\w-w)/2
			\y+(\h-h)/2
		EndIf
		If \x+w+d>DX
			\x=DX-w-10-d
		EndIf
		If \y+h+60+d>DY
			\y=DY-h-70-d
		EndIf
		If \x<=d
			\x=d
		EndIf
		If \y<=d
			\y=d
		EndIf
		\w=w
		\h=h
	EndWith
EndProcedure
Last edited by AZJIO on Thu Dec 16, 2021 2:00 pm, edited 2 times in total.
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Additional flag for MessageRequester to center requester on app window

Post by BarryG »

@Chi: Okay, I see what you mean. I've amended it to use short-circuit evaluation and lose the Goto; so the timing should be the same because anything less than 0 will immediately exit the procedure as required, along with passing the less-than-zero nCode value with it.

Code: Select all

If nCode > -1 And nCode = #HCBT_CREATEWND
  ; Blah
EndIf
ProcedureReturn CallNextHookEx_(0, nCode, wParam, lParam)
Post Reply