Hello,
To whom it may concern at McNeel (@curtisw, @stevebaer, @dale, @Gijs, @CallumSykes) can you please consider expanding the capabilities of the Rhino.UI.ViewportControl available in Eto.Forms to the full feature set of arguments available with a Rhino.View/Viewport.
Here’s a hacky example of what I would like… Obviously the “Eto UI visuals” of this example are ugly and I’m doing something really stupid to “psuedo embed it” but I’m making the point that they can be custom and whatever the developer wants to show via Eto would be possible both for the window/border itself and any UI elements on top of said viewport.
This hacky example spawns a floating Rhino.Viewport that “follows” the Eto.Form.
One cannot turn off the title bar/window style of the floating Rhino.Viewport like you can with a Rhino.UI.ViewportControl. (If the Rhino.View is set to floating = False, it does not show this title bar but then of course it’s “not floating”)
Examples of expanded capabilities:
-set display mode (can already do that with a VPC)
-set “WindowStyle” of the viewport (Default, Utility, NONE, etc.)
-run Rhino commands (since it’s a regular viewport… command line can have focus)
-use any rhino tool
-navigation controls that match the main Rhino instance (VPC seems to have hardcoded navigation mouse methods?)
-etc.
Who cares?
Well… the Rhino.UI.ViewportControl is great to have but really limited in that it is more of a “viewer”.
Having the ability to embed a fully featured Rhino.Viewport INTO an Eto.Form opens up so many great possibilities for Rhino Development.
Some more forum topics & reading on this subject:
Hacky Python Code:
#! python3
import Rhino
import Eto.Forms as forms
import Eto.Drawing as drawing
import System.Drawing as sdrawing
class MyEtoForm(forms.Form):
def __init__(self):
super().__init__()
self.Title = " Standard Viewport in Eto Form"
self.Size = drawing.Size(800, 600)
self.Location = drawing.Point(0, 0)
self.MovableByWindowBackground = True
self.WindowStyle = forms.WindowStyle.NONE
self.window_button_size = drawing.Size(32, 32)
self.window_button_bg_color = drawing.Color(180, 180, 180)
self.viewport = None
self.AddViewport()
self.CreateWindowButtons()
self.maximized = False
if self.WindowStyle:
# Add Transparent Style To The Form Background Panel
self.Styles.Add[forms.Panel]("transparent", self.MyFormStyler)
self.Style = "transparent"
# Subscribe to Eto form events
self.LocationChanged += self.OnFormMove
self.SizeChanged += self.OnFormResize
def OnMinimizeButtonClick(self, sender, e):
try:
self.Minimize() # Minimize Eto Form
except Exception as ex:
Rhino.RhinoApp.WriteLine(f"Minimize Button Exception: {ex}")
def OnMaximizeButtonClick(self, sender, e):
try:
self.maximized = not self.maximized
self.Maximize() if self.maximized else self.RestoreSize() # Maximize Eto Form
except Exception as ex:
Rhino.RhinoApp.WriteLine(f"Maximize Button Exception: {ex}")
def OnFormClose(self, sender, e):
try:
if self.viewport:
self.viewport.Close() # Close "Embedded" Viewport
self.Close() # Close Eto Form
except Exception as ex:
Rhino.RhinoApp.WriteLine(f"Close Button Exception: {ex}")
def CreateWindowButtons(self):
self.header_label = forms.Label()
self.header_label.Size = drawing.Size(300, self.window_button_size.Height)
self.header_label.Text = self.Title
self.header_label.BackgroundColor = self.window_button_bg_color
self.MinimizeButton = forms.Button()
self.MinimizeButton.Size = self.window_button_size
self.MinimizeButton.Text = "-"
self.MinimizeButton.BackgroundColor = self.window_button_bg_color
self.MaximizeButton = forms.Button()
self.MaximizeButton.Size = self.window_button_size
self.MaximizeButton.Text = "[ ]"
self.MaximizeButton.BackgroundColor = self.window_button_bg_color
self.CloseButton = forms.Button()
self.CloseButton.Size = self.window_button_size
self.CloseButton.Text = "X"
self.CloseButton.BackgroundColor = self.window_button_bg_color
self.MinimizeButton.Click += self.OnMinimizeButtonClick
self.MaximizeButton.Click += self.OnMaximizeButtonClick
self.CloseButton.Click += self.OnFormClose
self.layout = forms.DynamicLayout()
self.layout.Height = self.window_button_size.Height
self.layout.AddRow(self.header_label, self.MinimizeButton, self.MaximizeButton, self.CloseButton)
self.layout.AddRow(None)
self.Content = self.layout
def AddViewport(self):
if self.viewport:
self.viewport.Close()
self.display_mode = Rhino.Display.DisplayModeDescription.FindByName("Rendered")
# Create a Rhino viewport (floating)
view_rectangle = sdrawing.Rectangle(self.Location.X, (self.Location.Y + self.window_button_size.Height), self.Size.Width, (self.Size.Height - self.window_button_size.Height))
self.viewport = Rhino.RhinoDoc.ActiveDoc.Views.Add("embedded_viewport", Rhino.Display.DefinedViewportProjection.Perspective, view_rectangle, True)
self.viewport.TitleVisible = False
self.viewport.ActiveViewport.DisplayMode = self.display_mode
handle = self.viewport.Handle
Rhino.RhinoApp.WriteLine(f"viewport handle: {handle}")
# self.viewport.Topmost = True
# self.SendToBack = True
def OnFormMove(self, sender, e):
self.UpdateViewportPosition()
def OnFormResize(self, sender, e):
self.UpdateViewportPosition()
def UpdateViewportPosition(self):
if self.viewport:
self.AddViewport()
# Rhino.RhinoApp.WriteLine("Update Viewport Location On Form Move")
# Get the form's screen position and size
form_rect = self.Bounds
# Convert form position to screen coordinates
screen_position = forms.Screen.PrimaryScreen.Bounds.Location
new_position = sdrawing.Point(form_rect.X + screen_position.X, form_rect.Y + screen_position.Y)
new_size = sdrawing.Size(form_rect.Width - 16, form_rect.Height - 70)
# Update viewport position and size
self.viewport.Position = new_position
Rhino.RhinoApp.WriteLine(f"New Pos: {new_position}")
self.viewport.Size = new_size
# self.Topmost = False
# self.SendToBack = True
self.viewport.BringToFront = True
def RestoreSize(self):
try:
Rhino.RhinoApp.WriteLine("Call Restore Size")
except Exception as ex:
Rhino.RhinoApp.WriteLine(f"RestoreSize Exception: {ex}")
# UI STYLE CODE -------------------------------------------------------
# Handle Overall Background Transparency
def MyFormStyler(self, control):
if self.WindowStyle:
self.BackgroundColor = drawing.Colors.Transparent
window = control.ControlObject
if hasattr(window, "AllowsTransparency"):
window.AllowsTransparency = True
if hasattr(window, "Background"):
brush = window.Background.Clone()
brush.Opacity = 0
window.Background = brush
else:
color = window.BackgroundColor
window.BackgroundColor = color.FromRgba(0, 0, 0, 0)
# Create and show the Eto form
def EstablishForm():
main_toolbar = MyEtoForm()
main_toolbar.Owner = Rhino.UI.RhinoEtoApp.MainWindow
main_toolbar.Show()
if __name__ == "__main__":
EstablishForm()
Thank you for your time & consideration!
Cheers!