[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
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 ).
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
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:
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…
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.