Add Abilitity to 'Dock' HtmlDialogs


#1

Hi,

I was wondering if anyone else thinks it would be useful to be able to dock web dialogs. What I mean by docking is that when SketchUp is moved or resized the dialog would move with the main window. Perhaps this could be a setting of the dialog. Or it could ‘snap’ to any edge (sides, top or bottom). Also it would be nice to be able to add a Custom HtmlDialog to the tray. Then extensions could be ‘truly built in’.

Perhaps this could be a dialog style or a dialog setting. I would be in favor of allowing this property to change after the dialog is shown. Maybe in the tray there could be a button beside the X, to ‘pop out’ of the tray, or it could be restricted to code only to keep it consistent with other trays.

I envision some properties like this:

dlg.pinned = true       #pins the dialog at it's current location 
dlg.snap = true         #allows the user to snap the dialog to edges
dlg.snap_postion = 'left' #pins the dialog to the tray
puts dlg.snap_position? #[left,right,top,bottom,tray,pinned,or floating]

For putting into the tray you probably need to be able the choose which tray to have it added to.
I’m not sure if trays can currently be manipulated via the Ruby API or not.


#2

On second thought even docking outside SketchUp would be nice. Perhaps this is possible only on windows, because TeamViewer on Mac doesn’t dock like this.


#3

This has been requested as at least an enduser feature.

I would not care if there was even any API exposure to manipulate the trays or dialogs. Just would like the user to do it.

It does make sense however to let the developer set a flag or style when they instantiate the dialog to allow docking (or perhaps disallow docking if the default is made true for docking.)
ie:

dockable: true || false

So it would be an optional argument, that need only really be passed as false in those rare scenarios when the dialog is meant as a messagebox or something else and not a inspector type tool panel.

This will also lead to the need for a docked? and floating? instance methods.


Re, your ideas on .pinned() and .snap(),… would be incorrect, as those are tray functions, not inspector panel window functions. (The tray objects are not exposed to the API.)

For API exposure, we’d need something like:

my_dlg.dock_to( my_tray_object, ordinal_position )

… or:

my_tray_object.add_item( my_dlg, ordinal_position )

But this opens up the need for a Sketchup::trays collection, [] getter method, etc.

There needs to be a really strong use case to put this much work into the API.
Just being “nifty” isn’t enough.

And since trays are currently Windows platform only, it is unlikely to get API exposure until it is cross-platform.


#4

Thanks for the detailed explanation.

Docking would be a good start. I agree that tray manipulation would be a ‘cool’ thing but maybe not worth the hassle.

I don’t think the Mac users would complain about having trays. :smirk: Although I understand why it was not implemented on Mac. Windows made trays easier with their foundation classes.

Back to docking, on Mac it would be nice to be able to dock custom toolbars and ‘tray’ windows until a real tray is added. I know custom toolbar buttons can be put on the ‘built-in’ toolbar, but I haven’t fooled around enough on a Mac to know if this can be done through the API or not.

BTW: I think adding the trays in SU 2016 was one of the biggest improvements SketchUp ever made. (Sorry that might be stretched a little.)


#5

No, actually not. I think that trays was the first feature request I ever made back in the 7.0 cycle, like 8-9 years ago.


#6

Maybe there is already a way to do this, but if you could monitor the Main SketchUp window location with an observer, then docking could be done by the extension developer. Although I’m still not sure if you could move the windows while SketchUp is moving or only reposition them after SketchUp has been moved.


#7

Just tested it on Windows. It is possible to reposition a WebDialog while SketchUp is being moved.

Unfortunatly there is no way currently (correct me if I’m wrong) to check the position of the main SketchUp Window, or the WebDialog/HtmlDialog.

For a developer to usefully implement docking behavior he would also need to know the viewport size and position in relation to the application window.

Another issue with this method is that WebDialog.set_position also activates the WebDialog and brings it to the front.

I guess we’ll have to wait for Trimble to implement this feature.


#8

Ok, I’ll correct you. You can do it using Windows API calls.

I have an old extension that creates a nifty toolbar for the Ruby Console. It “snaps” the toolbar to the top-left corner of the console window (so it laid along the top of the window broder.) It re-positioned the toolbar after the console window was moved.

I has been so long I don’t remember exactly how I triggered the toolbar re-positioning.

It is on an old XP computer that I need to fix the CPU cooler on, before I can start it up again, and reclaim all my files on. (Just been putting this task off for more than a year.)


#10

I had thought of that possibility, but I must admit I don’t know much more about Windows API calls, than the basics.

So… any sample code from anyone would be useful.


#11

Ok after a little bit of online research I think I’m off to a good start.

Here is the code:

require 'Win32API'

FindWindow = Win32API.new('user32', 'FindWindow', ["P", "P"], "L")
GetWindowPos = Win32API.new("user32","GetWindowRect",["L","P"],"V")
SetWindowPos = Win32API.new("user32","SetWindowPos",["L","L","L","L","L","L","L"],"V")

def window_pos(window_title)
  r = "0"*16
  h = FindWindow.call(nil,window_title)
  GetWindowPos.call(h,r)
  return r.unpack("LLLL")
end

def set_window_pos(window_title,x,y,width=0,height=0)
  r = "0"*20
  temp = [x,y,height,width,16]
  h = FindWindow.call(nil,window_title)
  wp = window_pos(window_title)
  if height == 0 then height = wp[3] - wp[1] end
  if width == 0 then width = wp[2] - wp[0] end
  SetWindowPos.call(h,0,x,y,width,height,16)
end

def dock_to(docked_title, main_title)
  main_pos = window_pos(main_title)
  docked_pos = window_pos(docked_title)
  left = main_pos[0] - (docked_pos[2]-docked_pos[0])+15
  needs_move = false
  unless left == docked_pos[0] and main_pos[1] == docked_pos[1]
    set_window_pos(docked_title,left,main_pos[1]) 
  end
end

dock_timer = UI.start_timer(0.01,true){dock_to("Building Creator","Ruby Console")}

I’m still wondering if it is possible to find the coordinates to the viewport in relation to the main SketchUp app. That seems to be the only way to successfully ‘dock’ windows inside SketchUp without covering up the toolbar or tray.


#12

the ‘easiest’ way I’ve used in the past is to find the difference between system Mouse position and an SU tool’s onMouseMove(flags, x, y, view)

there’s a couple of topics on SketchUcation with code snippets and I’ll see if I can find my mac version…

john


#13

@john_drivenupthewall That would be very helpful. I can already get the width and height of the viewport.

v = SketchUp.active_model.acitve_view
@h = v.vpheight 
@w = v.vpwidth 

#14

Neil, everything in Windows is a window object. A button is a window object, for example. A toolbar is window object. etc. But each “kind” often has a special window class. So toolbar docking panes are windows of a certain MFC class. Likely the same for trays. Also the same for docked tray panels.

The big problem with trying to shoehorn html dialog windows into trays as a tray panel is that they are not the correct class object, and would have the correct functions the application expects them to have. This is not insurmountable, but usually requires some good knowledge of Windows and MFC programming.

Do it wrong, and you’ll be crashing SketchUp.


#15

Re, re-position trigger. I know I did not use a timer like your example.

I think I had several position “save” buttons (with numbers on them) and a double click on a certain button would re-snap the toolbar to the console’s upper left corner. The user (me because I never released it,) could save several “setups” for the console and it’s toolbar (ie, position, height and width,) and easily restore them with a click on one of the setup buttons. (A double-click would set the current position and size to that double-clicked button.)


#16

@Neil_Burkholder.

here’s a link with one of my examples…

john


#17

@DanRathbun Thanks Dan. Any adverse affect to using a timer? Loss of performance?

@john_drivenupthewall

I was able to find some code on SketchUcation to get the viewport window handle.

This code ‘docks’ the ruby console to the right of the viewport. Now I need to get a handle to the tray window so I can move the ‘docked’ window left on tray window flyout.

#win32api
require 'Win32API'

FindWindow = Win32API.new('user32', 'FindWindow', ["P", "P"], "L")
GetWindowPos = Win32API.new("user32","GetWindowRect",["L","P"],"V")
SetWindowPos = Win32API.new("user32","SetWindowPos",["L","L","L","L","L","L","L"],"V")
GetActiveWindow       = Win32API.new('User32', 'GetActiveWindow', '', 'L')
GetWindow             = Win32API.new('User32', 'GetWindow', 'LL', 'L')
GetClientRect         = Win32API.new('User32', 'GetClientRect', 'LP', 'I')
FindWindowEx          = Win32API.new('User32', 'FindWindowEx', 'LLPP', 'L')
GetWindowLong         = Win32API.new('User32', 'GetWindowLong', 'LI', 'L')
AdjustWindowRectEx    = Win32API.new('User32', 'AdjustWindowRectEx', 'PLIL', 'I')

active = GetActiveWindow.call()
owner = GetWindow.call(active, 4)
@main_hwnd = owner == 0 ? active : owner


def window_pos(window_ref)
  r = "0"*16
  if window_ref.is_a? Fixnum
   h = window_ref
  else
    h = FindWindow.call(nil,window_ref)
  end
  GetWindowPos.call(h,r)
  return r.unpack("LLLL")
end

def set_window_pos(window_ref,x,y,width=0,height=0)
  r = "0"*20
  temp = [x,y,height,width,16]
  if window_ref.is_a? Fixnum
    h = window_ref
  else
    h = FindWindow.call(nil,window_ref)
  end
  wp = window_pos(window_ref)
  if height == 0 then height = wp[3] - wp[1] end
  if width == 0 then width = wp[2] - wp[0] end
  SetWindowPos.call(h,0,x,y,width,height,16)
end

def dock_to(docked_title, main_title)
  main_pos = window_pos(main_title)
  docked_pos = window_pos(docked_title)
  left = main_pos[0] - (docked_pos[2]-docked_pos[0])+15
  needs_move = false
  unless left == docked_pos[0] and main_pos[1] == docked_pos[1] and main_pos[3] == docked_pos[3]
    set_window_pos(docked_title,left,main_pos[1],0,main_pos[3]-main_pos[1]) 
  end
end

def dock_viewport_right(hwnd)
    vp_pos = window_pos(get_viewport_handle)
    docked_pos = window_pos(hwnd)
    needs_move = false
    left = vp_pos[2] - (docked_pos[2]-docked_pos[0])+6
    unless left == docked_pos[0] and vp_pos[1] == docked_pos[1]
      set_window_pos(hwnd,left,vp_pos[1],0,vp_pos[3]-vp_pos[1])     
    end
end



def get_viewport_handle
  cname = case Sketchup.version.to_i
    when 6
      'AfxFrameOrView70u'
    when 7,8
      'AfxFrameOrView80u'
    when 13,14,15,16
      'AfxFrameOrView100u'
    else
      'AfxFrameOrView140u'     
  end
  return FindWindowEx.call(@main_hwnd, 0, cname, nil)
end


#dock_timer = UI.start_timer(0.01,true){dock_to("Building Creator","Ruby Console")}
dock_timer = UI.start_timer(0.01,true){dock_viewport_right(active)}

It looks like I have all the loose pieces no to make something wonderful.


#18

Of course there will be. You using clock cycles just to check position of a window. MS Windows uses window messaging, and I think it (the OS) would send a message to the window object that is has been moved. So ordinarily you could override the window’s message handler to trap that message and respond. But these windows are created by the API, and doing this could cause havoc.

As you know, it does not actually dock like the framework docks. Framework docking actually changes a window’s parent window to be the docking pane instead of the main application window. There are dedicated docking functions in the MFC framework.

But if SketchUp does not expect any old dialog window to be docked into a tray window, then it could choke especially during shutdown when it enumerates all it’s tray panels and tries to write their settings to the registry.

Same thing could happen during startup. How would SketchUp know how to start a certain extension that would create the dialog to be inserted into the big gaping hole in the tray layout? (My experience has been that SketchUp crashes with these kinds of hacks.)

You may be able to hack it to work with your particular setup, but you could never release it. The Extension Warehouse review would reject it. (If you release it elsewhere, and cause corruption of user’s application installs, don’t be surprised of legal action against you.)


#19

Thanks Dan.

I other words BAD IDEA.

I won’t waste more of my time on this hack then. I’ll submit a FR and wait for Trimble to implement this.

Just to be sure… were you talking about hacking the SU tray or that and the window repositioning timer?


#20

The hacking of the trays.

You are free to re-position if want. I’ve done that.


Sometimes there is no other way than a timer, but be sure to keep persistent reference to the timer while it’s “live”, so it can be switched off when unneeded.

Okay, so how would I do this fake docking ?

I’d create an invisible window, and set the parent window for the two “docking” windows, to be this invisible parent window. (Perhaps even create two sub-panes inside the parent window.)


#21

Interesting. As I said, I’m in learning mode on this one. I never fooled around much with windows API till yesterday. I’m not sure how to set a window as parent or how to intercept window messages. I guess I’ll need to do some more research.