Notifications
Clear all

[Closed] recreate build-in ui controls

This looks really interesting thanks! Would be amazing to be able to fully grab any part of max’s UI for our own custom tools without having to recreate it by hand!

could anyone give me an example where we need to recreate the max built-in ui? where we have to do it exactly by code (i mean not just simply based on its visual representation).
but if there is a reason we cannot do it without using GetWindowLong. Also we can’t use windows.getchildrenhwnd because it makes recursion where we don’t need it. We have to make our own function to know exactly where a parent is and what its children are.

we can parse the result of windows.getchildrenhwnd and make a tree of children, but as i said it’s easier to make our own function.

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

Posts: 0

I’m not sure where it would be useful, this is just a fun challenge for me.

Why not window.GetChildrenHwnd hWnd parent:hWnd ?

A small update: Added support for Checkbox, Groupbox, Label, RadioButton, ColorPicker.
A lot of things still don’t work, like grouping for radiobuttons, detecting state of checkboxes, subrollouts are currently converted to groupboxes, etc.


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	
	)
	
	fn grabWin32Button 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 WM_GETDLGCODE = 0x0087
		local btnType = windows.SendMessage hWnd WM_GETDLGCODE 0 0
		
		case btnType of
		(
			0x2040: --radioButton
			(
				format "RadioButtons rad% \"\" labels:#(\"%\") pos:[%, %] width:% height:%
" \
					numControls txt x y rect[3] rect[4] to:controlStrings
			)
			0x2000:	--checkBox
			(
				format "CheckBox chk% \"%\" pos:[%, %] width:% height:%
" \
					numControls txt x y rect[3] rect[4] to:controlStrings
				
			)
			0x100: --groupBox
			(
				format "GroupBox grp% \"%\" pos:[%, %] width:% height:%
" \
					numControls txt x y rect[3] rect[4] to:controlStrings
			)
		)
		numControls += 1
	)
	
	fn grabLabel 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 "Label lab% \"%\" pos:[%, %] width:% height:%
" \
			numControls txt x y rect[3] rect[4] to:controlStrings
		numControls += 1
	)
	
	fn grabColorPicker hWnd =
	(
		local rect = win32.GetWindowRect (ToIntPtr hWnd)
		local x = rect[1] - parentRect[1]
		local y = rect[2] - parentRect[2]
		format "ColorPicker clr% \"\" pos:[%, %] fieldWidth:% height:%
" \
			numControls x y rect[3] rect[4] to:controlStrings
		numControls += 1
	)
	
	fn grabRollup hWnd =
	(
		local rect = win32.GetWindowRect (ToIntPtr hWnd)
		local rollupTitle = (for w in windows.getChildrenHwnd hWnd parent:hWnd where w[4] == "RollupPanelTitle" collect w[1])[1]
		local txt = uiAccessor.getWindowText rollupTitle
		local x = rect[1] - parentRect[1]
		local y = rect[2] - parentRect[2]
		format "GroupBox grp% \"%\" pos:[%, %] width:% height:%
" \
			numControls txt x y rect[3] rect[4] to:controlStrings
		numControls += 1
	)
	
	local controlTypes = #("CustButton", "ComboBox", "Static", "Button", "ColorSwatch", "RollupPanel")
	local controlFunctions = #(grabButton, grabComboBox, grabLabel, grabWin32Button, grabColorPicker, grabRollup)
	
	fn grabControl hWnd =
	(
		local typeStr = UIAccessor.getWindowClassName hWnd
		local typeIndex = findItem controlTypes typeStr
		if (typeIndex > 0) do
		(
			controlFunctions[typeIndex] hWnd
		)
		for w in windows.getChildrenHwnd hWnd parent:hWnd where win32.IsWindowVisible (ToIntPtr w[1]) do grabControl w[1]
	)
	
	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 
		grabControl hWnd
		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

[i]Why not window.GetChildrenHwnd hWnd parent:hWnd ?

[/i]really why not use it? i just forgot about this option…


  local WM_GETDLGCODE = 0x0087 		
 local btnType = windows.SendMessage hWnd WM_GETDLGCODE 0 0
  

that’s smart. i like it. my first idea was to use GetWindowLong and check Button Styles

sorry that I’ve started that and I can’t follow it right now… lo, just tested! great work man! really!

In terms of the reason I wanted it, it’s that I’ve created a tool for the guys at work… something like a Modifiers listener. It finds all the modifiers from the scene, you can filter them by class, select assosiated objects, turn them On/Off etc… I was checking though if it’s possible to give them the option to get the same ui in the script so they can add global properties to all the modifiers.

Example: Let’s assume that there are three different modifiers in the scene 2 unique and 1 instanced to a couple of objects. With this option they can change things globaly. For example it would be cool to give them the option to multiply a parameter like the amount in the VRayDisplacement… etc

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

Posts: 0

Sorry if I’m raining on the parade, but do you mean something like this?
http://www.scriptspot.com/3ds-max/scripts/modifier-modifier-zorb

PS I haven’t used this tool, it just sounded similar from the description.

there are several modifier classes and its parameters that not automatically update if they are not opened in modifier panel. so the tool that you make cannot work in general case. but the making this kind of tool is very helpful to get a knowledge about different types of modifiers.
i agree with LO that trying of recreate a max dialog look is just a fun. i think that will be better for the ‘modifier inspection tool’ to give compact UI with parameters instead of showing sometimes not optimized original look.

that’s the first that came to me too. i’ve never used it either, but i made some similar tools for inspecting and editing some specific parameters of some specific modifiers/ca/etc. it makes sense.

i think that ‘Modifier Modifier Zorb’ is too much about everything. it was growing many years and it’s too heavy now. being encrypted it can not be modified and localized for any specific tasks. that’s why many coders continue write their own inspection tools.

Before I start this tool, I’ve told them that there are a couple of tools online and I was told that they have tried a bunch of them and none of them was very usefull… That’s why I was asked to create a new one closer to our needs.

DenisT, my first though was to recreate the most commonly used Modifiers and for all the others to have a listener like ModZorb…

Thanks both of you! I really appreciate your help!

3 Replies
 lo1
(@lo1)
Joined: 11 months ago

Posts: 0

I understand how it might be possible to copy a UI of a modifier programatically, but how would you go about hooking up the handlers to the original modifier?

(@denist)
Joined: 11 months ago

Posts: 0

Nick doesn’t need as i understood any hooking (or binding) ui controls. He wants to make fake UI that looks like original and use that dialog control handles to get/set a modifier parameters.

(@sinokgr)
Joined: 11 months ago

Posts: 0

exactly!

That was my first post… I want the positions, sizes, ranges etc…
The binding can’t be done with handles cause you can’t have all the modifiers open at the same time. If you have two vrayDisplacements for example that aren’t instanced you can’t get the UI in the modify panel… If I could recreate all the UIs dynamically, a workaround could probably be to select only one object with the modifier, create the UI, select another modifier with the other UI, copy that too… etc untill I create all the UIs for the selected modifier classes. But still it would be a fake UI with no handles assosiated with it.

I’ll ask if I can show you a video with the features or even if I can share the tool with you.

If I go with a solution with “pre-generated” UIs for the most commonly modifiers, as I’ve told, it’s not needed to go with something that complex with handles etc… I’ll just merge my pre-saved UI and I’ll change properites from this UI.

About a year ago I have copied by hand the VRayLight’s UI and I was doing the same thing. Because I remember how time consuming was to copy the UI by hand, that’s why I asked if there was any other way…

Page 2 / 3