mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 15:14:35 +00:00
Implementation of Nils' UI work for updated theming, functionality and style for the editors suite
This commit is contained in:
parent
dc1d6e7d9d
commit
33f35d35d4
908 changed files with 15381 additions and 3065 deletions
|
|
@ -40,6 +40,8 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
HoverNodeColor = "255 255 255 255";
|
||||
|
||||
new GuiWindowCollapseCtrl(RiverEditorTreeWindow) {
|
||||
canCollapse = "0";
|
||||
canMove = "0";
|
||||
internalName = "";
|
||||
canSaveDynamicFields = "0";
|
||||
Enabled = "1";
|
||||
|
|
@ -47,10 +49,10 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
Profile = "ToolsGuiWindowProfile";
|
||||
HorizSizing = "windowRelative";
|
||||
VertSizing = "windowRelative";
|
||||
Position = getWord($pref::Video::mode, 0) - 209
|
||||
SPC getWord(EditorGuiToolbar.extent, 1) - 1;
|
||||
Extent = "210 167";
|
||||
MinExtent = "210 100";
|
||||
Position = getWord($pref::Video::mode, 0) - 360
|
||||
SPC getWord(EditorGuiToolbar.extent, 1) + 6;
|
||||
Extent = "360 167";
|
||||
MinExtent = "300 100";
|
||||
canSave = "1";
|
||||
isDecoy = "0";
|
||||
Visible = "1";
|
||||
|
|
@ -64,13 +66,12 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
AnchorRight = "0";
|
||||
resizeWidth = "1";
|
||||
resizeHeight = "1";
|
||||
canMove = "1";
|
||||
canClose = "0";
|
||||
canMinimize = "0";
|
||||
canMaximize = "0";
|
||||
minSize = "50 50";
|
||||
EdgeSnap = "1";
|
||||
text = "Rivers";
|
||||
text = ":: River Editor - Rivers";
|
||||
|
||||
/*
|
||||
new GuiBitmapButtonCtrl() {
|
||||
|
|
@ -122,7 +123,7 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
};
|
||||
|
||||
*/
|
||||
new GuiContainer(){
|
||||
new GuiContainer(RiverEditorTreeWPanel){
|
||||
profile = "ToolsGuiDefaultProfile";
|
||||
Position = "5 25";
|
||||
Extent = "200 120";
|
||||
|
|
@ -190,8 +191,103 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
};
|
||||
};
|
||||
};
|
||||
// window / panel buttons
|
||||
new GuiBitmapButtonCtrl(RiverEditorGui_UnDockBtn) {
|
||||
canSaveDynamicFields = "0";
|
||||
internalName = "";
|
||||
Enabled = "1";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiButtonProfile";
|
||||
HorizSizing = "left";
|
||||
VertSizing = "bottom";
|
||||
Position = "330 1";
|
||||
Extent = "18 18";
|
||||
MinExtent = "8 8";
|
||||
canSave = "1";
|
||||
Visible = "0";
|
||||
Command = "RiverEditorGui.releaseSidePanel();";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Detach Sidepanel";
|
||||
hovertime = "1000";
|
||||
bitmapAsset = "ToolsModule:panel_undock_n_image";
|
||||
text = "";
|
||||
groupNum = "-1";
|
||||
buttonType = "PushButton";
|
||||
useMouseEvents = "0";
|
||||
};
|
||||
new GuiBitmapButtonCtrl(RiverEditorGui_DockBtn) {
|
||||
canSaveDynamicFields = "0";
|
||||
internalName = "";
|
||||
Enabled = "1";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiButtonProfile";
|
||||
HorizSizing = "left";
|
||||
VertSizing = "bottom";
|
||||
Position = "330 1";
|
||||
Extent = "18 18";
|
||||
MinExtent = "8 8";
|
||||
canSave = "1";
|
||||
Visible = "0";
|
||||
Command = "RiverEditorGui.dockSidePanel();";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Dock Sidepanel";
|
||||
hovertime = "1000";
|
||||
bitmapAsset = "ToolsModule:panel_dock_n_image";
|
||||
text = "";
|
||||
groupNum = "-1";
|
||||
buttonType = "PushButton";
|
||||
useMouseEvents = "0";
|
||||
};
|
||||
new GuiBitmapButtonCtrl(RiverEditorGui_HideBtn) {
|
||||
canSaveDynamicFields = "0";
|
||||
internalName = "";
|
||||
Enabled = "1";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiButtonProfile";
|
||||
HorizSizing = "left";
|
||||
VertSizing = "bottom";
|
||||
Position = "312 1";
|
||||
Extent = "18 18";
|
||||
MinExtent = "8 8";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
Command = "RiverEditorGui.hideSidePanel();";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Hide Sidepanel";
|
||||
hovertime = "1000";
|
||||
bitmapAsset = "ToolsModule:panel_hide_n_image";
|
||||
text = "";
|
||||
groupNum = "-1";
|
||||
buttonType = "PushButton";
|
||||
useMouseEvents = "0";
|
||||
};
|
||||
new GuiBitmapButtonCtrl(RiverEditorGui_ShowBtn) {
|
||||
canSaveDynamicFields = "0";
|
||||
internalName = "";
|
||||
Enabled = "1";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiButtonProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
Position = "4 1";
|
||||
Extent = "18 18";
|
||||
MinExtent = "8 8";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
Command = "RiverEditorGui.showSidePanel();";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Show Sidepanel";
|
||||
hovertime = "1000";
|
||||
bitmapAsset = "ToolsModule:panel_show_n_image";
|
||||
text = "";
|
||||
groupNum = "-1";
|
||||
buttonType = "PushButton";
|
||||
useMouseEvents = "0";
|
||||
};
|
||||
};
|
||||
new GuiWindowCollapseCtrl(RiverEditorOptionsWindow) {
|
||||
canCollapse = "0";
|
||||
canMove = "0";
|
||||
internalName = "Window";
|
||||
canSaveDynamicFields = "0";
|
||||
Enabled = "1";
|
||||
|
|
@ -199,10 +295,10 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
Profile = "ToolsGuiWindowProfile";
|
||||
HorizSizing = "windowRelative";
|
||||
VertSizing = "windowRelative";
|
||||
Position = getWord($pref::Video::mode, 0) - 209
|
||||
SPC getWord(EditorGuiToolbar.extent, 1) + getWord(RiverEditorTreeWindow.extent, 1) - 2;
|
||||
Extent = "210 530";
|
||||
MinExtent = "210 300";
|
||||
Position = getWord($pref::Video::mode, 0) - 360
|
||||
SPC getWord(EditorGuiToolbar.extent, 1) + getWord(RiverEditorTreeWindow.extent, 1) + 6;
|
||||
Extent = "360" SPC getWord($pref::Video::mode, 1) - getWord(RiverEditorTreeWindow.extent, 1) - getWord(EditorGuiToolbar.extent, 1) - 65;
|
||||
MinExtent = "300 300";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
|
|
@ -215,15 +311,14 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
AnchorRight = "0";
|
||||
resizeWidth = "1";
|
||||
resizeHeight = "1";
|
||||
canMove = "1";
|
||||
canClose = "0";
|
||||
canMinimize = "0";
|
||||
canMaximize = "0";
|
||||
minSize = "50 50";
|
||||
EdgeSnap = "1";
|
||||
text = "Properties";
|
||||
text = ":: River Editor - Properties";
|
||||
|
||||
new GuiContainer(){ //Node Properties
|
||||
new GuiContainer(RiverEditorOptionsPanel){ //Node Properties
|
||||
isContainer = "1";
|
||||
Profile = "inspectorStyleRolloutDarkProfile";
|
||||
HorizSizing = "width";
|
||||
|
|
@ -234,7 +329,7 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
Margin = "3 3 3 3";
|
||||
|
||||
new GuiTextCtrl(){
|
||||
Profile = "ToolsGuiDefaultProfile";
|
||||
Profile = "EditorTextProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
Position = "5 0";
|
||||
|
|
@ -315,7 +410,7 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
AltCommand = "RiverEditorGui.editNodeDetails();";
|
||||
};
|
||||
};
|
||||
new GuiContainer(){ //River Properties
|
||||
new GuiContainer(RiverEditorPropPanel){ //River Properties
|
||||
isContainer = "1";
|
||||
Profile = "inspectorStyleRolloutDarkProfile";
|
||||
HorizSizing = "width";
|
||||
|
|
@ -326,7 +421,7 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
Margin = "0 0 3 3";
|
||||
|
||||
new GuiTextCtrl(){
|
||||
Profile = "ToolsGuiDefaultProfile";
|
||||
Profile = "EditorTextProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
Position = "5 0";
|
||||
|
|
@ -335,7 +430,7 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
};
|
||||
};
|
||||
|
||||
new GuiScrollCtrl() {
|
||||
new GuiScrollCtrl(RiverEditorPropScroll) {
|
||||
canSaveDynamicFields = "0";
|
||||
Enabled = "1";
|
||||
isContainer = "1";
|
||||
|
|
@ -390,11 +485,11 @@ $guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) {
|
|||
canSaveDynamicFields = "0";
|
||||
Enabled = "1";
|
||||
isContainer = "0";
|
||||
Profile = "GuiInspectorFieldInfoMLTextProfile";
|
||||
position = "8" SPC getWord(RiverEditorOptionsWindow.extent, 1) - 40;
|
||||
HorizSizing = "width";
|
||||
VertSizing = "top";
|
||||
Position = "1 485";
|
||||
Extent = "202 42";
|
||||
Extent = "330 42";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
|
|
|
|||
|
|
@ -25,6 +25,267 @@ $River::wireframe = true;
|
|||
$River::showSpline = true;
|
||||
$River::showRiver = true;
|
||||
$River::showWalls = true;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function RiverEditorGui::onWake(%this)
|
||||
{
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
|
||||
if(EditorSettings.value( "WorldEditor/forceSidebarToSide" ) == 1)
|
||||
{
|
||||
// Let's dock the side panel to the right side
|
||||
%this.docked = false;
|
||||
%this.resizing = true;
|
||||
%this.dockSidePanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's release the side panel so it can be moved
|
||||
%this.docked = true;
|
||||
%this.resizing = false;
|
||||
%this.releaseSidePanel();
|
||||
}
|
||||
}
|
||||
|
||||
function RiverEditorGui::maxSize(%this, %window)
|
||||
{
|
||||
// Resize the windows to the max height
|
||||
// and force these to the right side if set
|
||||
if(EditorSettings.value( "WorldEditor/forceSidebarToSide" ) == 1 && %this.resizing == true)
|
||||
{
|
||||
// prevent onResize after a resize
|
||||
%this.resizing = false;
|
||||
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
%top = EditorGuiToolbar.extent.y + 6;
|
||||
%bottom = %top + 65 - 6;
|
||||
%maxHeight = Canvas.extent.y - %top - %bottom;
|
||||
|
||||
// --- Fixed window (top) ------------------------------------------------
|
||||
// put it back if it moved
|
||||
%fixedWindow.position.x = Canvas.extent.x - %fixedWindow.extent.x;
|
||||
%fixedWindow.position.y = %top;
|
||||
|
||||
// don't go beyond the canvas
|
||||
if(%fixedWindow.extent.y > %maxHeight)
|
||||
%fixedWindow.extent.y = %maxHeight - %fluidWindow.extent.y;
|
||||
|
||||
%position = %fixedWindow.position.x SPC %fixedWindow.position.y;
|
||||
%extent = %window.extent.x SPC %fixedWindow.extent.y;
|
||||
%fixedWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
// --- Fluid window (bottom) ---------------------------------------------
|
||||
// position is relative to the top window
|
||||
%position = %fixedWindow.position.x SPC %fixedWindow.extent.y + %top;
|
||||
%extent = %window.extent.x SPC Canvas.extent.y - %fixedWindow.extent.y - %bottom;
|
||||
%fluidWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
// --- AssetBrowser window ----------------------------------------------
|
||||
if(isObject(AssetBrowserWindow))
|
||||
{
|
||||
// Only resize the AssetBrowser if it's docked
|
||||
if(AssetBrowserWindow.docked == true)
|
||||
{
|
||||
// The width is relative to the sidepanel
|
||||
%browserWidth = Canvas.extent.x - %extent.x;
|
||||
%browserHeight = AssetBrowserWindow.extent.y;
|
||||
%browserPosY = Canvas.extent.y - AssetBrowserWindow.extent.y - 33;
|
||||
AssetBrowserWindow.resize(0, %browserPosY, %browserWidth, %browserHeight);
|
||||
}
|
||||
}
|
||||
// --- Windowed Console --------------------------------------------------
|
||||
if(isObject(windowConsoleControl))
|
||||
{
|
||||
// Only resize the AssetBrowser if it's docked
|
||||
if(windowConsoleControl.docked == true)
|
||||
{
|
||||
// The width is relative to the sidepanel
|
||||
%consoleWidth = Canvas.extent.x - %extent.x;
|
||||
%consoleHeight = windowConsoleControl.extent.y;
|
||||
%consolePosY = Canvas.extent.y - windowConsoleControl.extent.y - 33;
|
||||
windowConsoleControl.resize(0, %consolePosY, %consoleWidth, %consoleHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function RiverEditorTreeWindow::onMouseDragged(%this)
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
|
||||
if(%parent.panelHidden == true)
|
||||
{
|
||||
%parent.showSidePanel();
|
||||
}
|
||||
|
||||
if(%parent.resizing == false && %parent.docked == true)
|
||||
{
|
||||
%parent.resizing = true;
|
||||
%parent.maxSize(%this);
|
||||
}
|
||||
}
|
||||
|
||||
function RiverEditorOptionsWindow::onMouseDragged(%this)
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
|
||||
if(%parent.panelHidden == true)
|
||||
{
|
||||
%parent.showSidePanel();
|
||||
}
|
||||
|
||||
if(%parent.resizing == false && %parent.docked == true)
|
||||
{
|
||||
%parent.resizing = true;
|
||||
%parent.maxSize(%this);
|
||||
}
|
||||
}
|
||||
|
||||
function RiverEditorGui::onResize(%this, %newPosition, %newExtent)
|
||||
{
|
||||
// Window to focus on (mostly the fluid window)
|
||||
%window = RiverEditorOptionsWindow;
|
||||
|
||||
if(%window.panelHidden == true)
|
||||
{
|
||||
%window.showSidePanel();
|
||||
}
|
||||
|
||||
if(%this.resizing == false && %this.docked == true)
|
||||
{
|
||||
// Only resize once
|
||||
%this.resizing = true;
|
||||
%this.maxSize(%window);
|
||||
}
|
||||
}
|
||||
|
||||
function RiverEditorGui::dockSidePanel()
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
|
||||
if(%parent.docked == true)
|
||||
return;
|
||||
|
||||
// Move and resize the window(s)
|
||||
%parent.resizing = true;
|
||||
%parent.maxSize(%fluidWindow);
|
||||
|
||||
%parent.docked = true;
|
||||
%fluidWindow.onMouseDragged();
|
||||
|
||||
// Lock the windows in place
|
||||
%fixedWindow.canCollapse = "0";
|
||||
%fixedWindow.canMove = "0";
|
||||
|
||||
%fluidWindow.canCollapse = "0";
|
||||
%fluidWindow.canMove = "0";
|
||||
|
||||
RiverEditorGui_UnDockBtn.Visible = "1";
|
||||
RiverEditorGui_DockBtn.Visible = "0";
|
||||
|
||||
RiverEditorGui_showBtn.Visible = "0";
|
||||
RiverEditorGui_hideBtn.Visible = "1";
|
||||
}
|
||||
|
||||
function RiverEditorGui::releaseSidePanel()
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
|
||||
if(%parent.docked == false)
|
||||
return;
|
||||
|
||||
// Unlock the windows so that be moved
|
||||
%fixedWindow.canCollapse = "1";
|
||||
%fixedWindow.canMove = "1";
|
||||
|
||||
%fluidWindow.canCollapse = "1";
|
||||
%fluidWindow.canMove = "1";
|
||||
|
||||
RiverEditorGui_UnDockBtn.Visible = "0";
|
||||
RiverEditorGui_DockBtn.Visible = "1";
|
||||
|
||||
RiverEditorGui_showBtn.Visible = "0";
|
||||
RiverEditorGui_hideBtn.Visible = "0";
|
||||
|
||||
// Let's do a small resize so it's visually clear we're undocking
|
||||
%position = %fixedWindow.position.x - 6 SPC %fixedWindow.position.y + 6;
|
||||
%extent = %fixedWindow.extent.x SPC %fixedWindow.extent.y;
|
||||
%fixedWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
%position = %fluidWindow.position.x - 6 SPC %fluidWindow.position.y + 6;
|
||||
%extent = %fluidWindow.extent.x SPC %fluidWindow.extent.y - 12;
|
||||
%fluidWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
%parent.docked = false;
|
||||
%parent.resizing = false;
|
||||
}
|
||||
|
||||
function RiverEditorGui::hideSidePanel()
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
|
||||
RiverEditorGui_showBtn.Visible = "1";
|
||||
RiverEditorGui_hideBtn.Visible = "0";
|
||||
|
||||
// hide the content of the panels
|
||||
%fixedWindow.titleText = %fixedWindow.text;
|
||||
%fluidWindow.titleText = %fluidWindow.text;
|
||||
|
||||
%fixedWindow.text = "";
|
||||
RiverEditorTreeWPanel.Visible = "0";
|
||||
|
||||
%fluidWindow.text = "";
|
||||
RiverEditorOptionsPanel.Visible = "0";
|
||||
RiverEditorPropPanel.Visible = "0";
|
||||
RiverEditorPropScroll.Visible = "0";
|
||||
|
||||
// Let's do a resize so that the panel is collapsed to the side
|
||||
%position = Canvas.extent.x - 24 SPC %fixedWindow.position.y;
|
||||
%extent = %fixedWindow.extent.x SPC %fixedWindow.extent.y;
|
||||
%fixedWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
%position = Canvas.extent.x - 24 SPC %fluidWindow.position.y;
|
||||
%extent = %fluidWindow.extent.x SPC %fluidWindow.extent.y;
|
||||
%fluidWindow.resize(%position.x, %position.y, %extent.x, %extent.y);
|
||||
|
||||
%parent.panelHidden = true;
|
||||
}
|
||||
|
||||
function RiverEditorGui::showSidePanel()
|
||||
{
|
||||
%parent = RiverEditorGui;
|
||||
%fixedWindow = RiverEditorTreeWindow;
|
||||
%fluidWindow = RiverEditorOptionsWindow;
|
||||
|
||||
RiverEditorGui_showBtn.Visible = "0";
|
||||
RiverEditorGui_hideBtn.Visible = "1";
|
||||
|
||||
// show the content of the panels
|
||||
// hide the content of the panels
|
||||
%fixedWindow.text = %fixedWindow.titleText;
|
||||
RiverEditorTreeWPanel.Visible = "1";
|
||||
|
||||
%fluidWindow.text = %fluidWindow.titleText;
|
||||
RiverEditorOptionsPanel.Visible = "1";
|
||||
RiverEditorPropPanel.Visible = "1";
|
||||
RiverEditorPropScroll.Visible = "1";
|
||||
|
||||
%parent.resizing = true;
|
||||
%parent.maxSize(%fluidWindow);
|
||||
|
||||
%parent.panelHidden = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function RiverEditorGui::onEditorActivated( %this )
|
||||
{
|
||||
|
|
@ -182,7 +443,7 @@ function RiverInspector::onInspectorFieldModified( %this, %object, %fieldName, %
|
|||
|
||||
function RiverInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc )
|
||||
{
|
||||
RiverFieldInfoControl.setText( "<font:Arial Bold:14>" @ %fieldName @ "<font:Arial Italic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc );
|
||||
RiverFieldInfoControl.setText( "<font:" @ $Gui::fontTypeBold @ ":16>" @ %fieldName @ "<font:" @ $Gui::fontTypeItalic @ ":16> (" @ %fieldTypeStr @ ") " NL "<font:" @ $Gui::fontTypeRegular @ ":16>" @ %fieldDoc );
|
||||
}
|
||||
|
||||
function RiverTreeView::onInspect(%this, %obj)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue