Notifications
Clear all

[Closed] recreate build-in ui controls

Hello there,

I was wondering if there is any way to “copy” the exact same UI from an object/modifier/etc.

I know that I can get an Array with all the properties of an object/modifier/etc using getPropNames and from that I can recreate the UI by checking the classOf the property and either with rolloutCreator or with just my own string, to create controls. But is there any way that I can get positions, size of the original controls or probably the spinner range and stuff?

I’m afraid that the answer will be no… but… hope dies last

27 Replies
 lo1

Yes, sounds very possible. Not at all simple, but possible.

Basically get the window handle of the UI you want to copy and start iterating over its children ,checking their class type, size, and text. The have a function that translates each type of class to a maxscript friendly control type and viola.

You will require some compiled win32 functions, such as getWindowRect. You can search this forum for premade code snippets.

Thanks a lot lo!! I also found this very helpfull topic too( http://forums.cgsociety.org/showthread.php?t=746512 ).

 lo1

Your idea inspired me to write something small like this:

try(destroyDialog uiGrabber)catch()
rollout uiGrabber "UI Grabber"
(
	fn generateAssembly = 
	(
		str="using System;
"
		str+="using System.Runtime.InteropServices;
"
		str+="using System.Windows.Forms;
"
		str+="using System.Text;
"
		str+="using System.Collections.Generic;
"
		
		str+="namespace Win32Utils
"
		str+="{
"
		str+="class Win32Helper
"
		str+="{
"			
		str+="[DllImport(\"user32.dll\", SetLastError = true, CharSet = CharSet.Auto)]
"
		str+="private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
"

        str+="public static string GetClassName(IntPtr hWnd)
"
        str+="{
"
        str+=" 	  StringBuilder sb = new StringBuilder(255);
"
        str+="    GetClassName(hWnd, sb, 255);
"
        str+="    return sb.ToString();
"
        str+="}
"
		
		str+="[StructLayout(LayoutKind.Sequential)]
"
		str+="public struct POINT	{ public int X;	public int Y; }
"
		
		str+="public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
"     
		
		str+="[DllImport(\"user32.dll\")]
"
		str+="public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
"
		
		str+="[DllImport(\"user32.dll\")]
"
		str+="public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
"
		
		str+="[DllImport(\"user32.dll\")]
"
		str+="public static extern bool IsWindowVisible(IntPtr hWnd);
" 
		
		str+="[DllImport(\"user32.dll\")]
"
		str+="public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
" 
		
		str+="public static int[] GetWindowRect(IntPtr hWnd)
" 
		str+="{
"
		str+="		RECT r = new RECT();
"
		str+="		GetWindowRect(hWnd, out r);
"
		str+="		return new int[] {r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top};
"
		str+="}
"
		
		str+="}
"
		str+="}
"

		local csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
		local compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
		compilerParams.ReferencedAssemblies.addRange #("System.dll","System.Windows.Forms.dll","System.Drawing.dll")
		compilerParams.GenerateInMemory = on
		local compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(str)
		
		for er =0 to compilerResults.errors.count-1 do print (compilerResults.errors.item[er].tostring())
		return compilerResults.CompiledAssembly.createInstance "Win32Utils.Win32Helper"
	)
	
	local win32 = generateAssembly()
	local controlStrings = stringStream ""
	local numControls = 0
	local parentRect
	
	
	
	fn ToIntPtr hWnd =
	(
		return (dotnetObject "System.IntPtr" hWnd)
	)
	
	fn grabButton hWnd =
	(
		local rect = win32.GetWindowRect (ToIntPtr hWnd)
		local txt = uiAccessor.getWindowText hWnd
		local x = rect[1] - parentRect[1]
		local y = rect[2] - parentRect[2]
		format "Button btn% \"%\" pos:[%, %] width:% height:%
" numControls txt x y rect[3] rect[4] to:controlStrings
		numControls += 1
	)
	
	fn grabComboBox hWnd =
	(
		local rect = win32.GetWindowRect (ToIntPtr hWnd)
		local txt = uiAccessor.getWindowText hWnd
		local x = rect[1] - parentRect[1]
		local y = rect[2] - parentRect[2]
		
		local CB_GETCOUNT = 0x0146
		local CB_GETLBTEXT = 0x0148
		local numItems = windows.SendMessage hWnd CB_GETCOUNT 0 0
		local marshal = dotnetClass "System.Runtime.InteropServices.Marshal"
		
		format "DropDownList ddl% \"%\" pos:[%, %] width:% height:% items:#(" numControls txt x y rect[3] rect[4] to:controlStrings
		numControls += 1
		
		for i = 1 to numItems do
		(
			local ptr = marshal.AllocHGlobal 255 asDotNetObject:on
			windows.sendMessage hWnd CB_GETLBTEXT (i-1) (ptr.ToInt64())
			local str = marshal.PtrToStringANSI ptr
			format "\"%\"" str to:controlStrings
			if (i != numItems) do format "," str to:controlStrings
			marshal.FreeHGlobal ptr
		)
		
		format ")
" to:controlStrings	
	)
	
	local controlTypes = #("CustButton", "ComboBox")
	local controlFunctions = #(grabButton, grabComboBox)
	
	fn grabControl hWnd =
	(
	
		local typeStr = UIAccessor.getWindowClassName hWnd
		local typeIndex = findItem controlTypes typeStr
		if (typeIndex > 0) do
		(
			controlFunctions[typeIndex] hWnd
		)
	)
	
	fn grabUI hWnd =
	(
		numControls = 0
		controlStrings = stringStream ""
		parentRect = win32.getWindowRect (ToIntPtr hWnd)
		format "rollout GrabbedRollout \"Grabbed Rollout\" width:% height:%
(
" parentRect[3] parentRect[4] to:controlStrings 
		for w in windows.getChildrenHwnd hWnd where win32.IsWindowVisible (ToIntPtr w[1]) do grabControl w[1]
		format "
)" to:controlStrings
		setClipBoardText (controlStrings as string)
	)
	
	editText txtHandle "Root Handle" width:150
	button btnGrab "Grab!" width:70 height:25

	on btnGrab pressed do
	(
		local hWnd = txtHandle.text as integer
		if hWnd == undefined or not (UIAccessor.IsWindow hWnd) do return "Invalid window handle!"
		grabUI hWnd
	)

)
createDialog uiGrabber

Obviously there is still much to be done. Only buttons and comboboxes are currently supported, and there is no support for sub-rollouts. But it’s a start…

WOW that’s some really good looking code! thank you very much lo! You saved me from a lot of trail n error!

Unfortunatelly I’m not sure if I can continue this before next weekend, but I’ll try!

cheers

 lo1

I might continue it myself, it’s a fun project. I’ll update if I do.

heheh cool

seriously now?! I just saw the WindowShopper! Great work! thanks man!

But with the help of your script I’ve realised something I have noticed yesterday when I was looking the results from hWND… The controls are breaked into seperate parts. A spinner for example, if I’m right, is seperated into a Label, a textBox and the spinner. But most of the controls are represented as “buttons”…:sad:

 lo1

Yes, you’re right. Some more intricate logic would be needed to capture those properly.

Y-pos probably, eventhough you can’t be sure if they are just aligned or if they are the same control and what type of control…

1 Reply
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

A spinner is basically just a ‘SpinnerControl’ and a ‘CustEdit’. It is not impossible to check if the ‘SpinnerControl’ is exactly in a certain position relative to the ‘CustEdit’.

The label can be added separately. It doesn’t have to be the text of the spinner.

Page 1 / 3