Download CommonLua/UI/Dev/uiDevDock.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 14.7 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/UI/Dev/uiDevDock.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/UI/Dev/uiDevDock.lua
-
curl -L -o uiDevDock.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/UI/Dev/uiDevDock.lua
14.7 kB
| DefineClass.DevDock = { | |
| __parents = { "XDialog" }, | |
| ZOrder = 100, | |
| IdNode = true, | |
| Background = white, | |
| BorderWidth = 1, | |
| BorderColor = black, | |
| MinWidth = 32, | |
| MinHeight = 32, | |
| MaxWidth = 32, | |
| MaxHeight = 32, | |
| HAlign = "left", | |
| VAlign = "top", | |
| dock_side = false, | |
| popup = false, | |
| } | |
| function DevDock:Init() | |
| local dock = self | |
| local mover = XMoveControl:new({ | |
| Id = "idMover", | |
| last_delta = false, | |
| any_movement = false, | |
| popup_visible_before_drag = false, | |
| OnDragDelta = function(self, delta) | |
| local dlg = GetDialog(self) | |
| self.last_delta = delta | |
| if not self.any_movement and delta ~= point20 then | |
| self.any_movement = true | |
| self.popup_visible_before_drag = dlg.popup:GetVisible() | |
| dlg.popup:SetVisible(false) | |
| end | |
| dlg:UpdatePopupAnchor() | |
| end, | |
| OnDragStart = function(self) | |
| local dlg = GetDialog(self) | |
| end, | |
| OnDragEnd = function(self) | |
| local dlg = GetDialog(self) | |
| if not self.any_movement then | |
| dlg:OnClick() | |
| else | |
| dlg:SnapToEdge() | |
| end | |
| self.last_delta = false | |
| self.any_movement = false | |
| end, | |
| }, self) | |
| local btn = XText:new({ | |
| Id = "idArrow", | |
| }, mover) | |
| btn:SetText("?") | |
| self.popup = DevDockPopup:new({ | |
| Id = "idPopup", | |
| dock = self, | |
| }, self) | |
| self.popup:SetVisible(table.get(LocalStorage, "DevDock", "Popup")) | |
| local dock_pos = table.get(LocalStorage, "DevDock", "DockPos") | |
| local dock_side = table.get(LocalStorage, "DevDock", "DockSide") | |
| if dock_pos and dock_side then | |
| mover:ApplyOffsetToTarget(self, dock_pos) | |
| self:SetDockSide(dock_side) | |
| end | |
| end | |
| function DevDock:OnLayoutComplete() | |
| self:UpdatePopupAnchor() | |
| self.OnLayoutComplete = empty_func | |
| end | |
| function DevDock:Flash() | |
| if self:IsThreadRunning("Flash") then return end | |
| if self:IsThreadRunning("Notify") then return end | |
| self:CreateThread("Flash", function(self) | |
| local win = self:ResolveId("idArrow") | |
| local original = win:GetBackground() | |
| local interval = 100 | |
| for i=1,5 do | |
| win:SetBackground(black) | |
| Sleep(interval) | |
| win:SetBackground(original) | |
| Sleep(interval) | |
| end | |
| win:SetBackground(original) | |
| end, self) | |
| end | |
| function DevDock:Notify() | |
| if self:IsThreadRunning("Notify") then return end | |
| if self.popup:GetVisible() then return end | |
| self:CreateThread("Notify", function(self) | |
| local win = self:ResolveId("idArrow") | |
| local original = win:GetBackground() | |
| local interval = 500 | |
| while win.window_state ~= "destroying" do | |
| win:SetBackground(yellow) | |
| Sleep(interval) | |
| win:SetBackground(original) | |
| Sleep(interval) | |
| end | |
| end, self) | |
| end | |
| function DevDock:OnClick() | |
| local popup = self.popup | |
| local visible = popup:GetVisible() | |
| popup:SetVisible(not visible) | |
| self:UpdatePopupAnchor() | |
| if self.popup:GetVisible() then | |
| if self:IsThreadRunning("Notify") then | |
| self:DeleteThread("Notify") | |
| self:ResolveId("idArrow"):SetBackground(white) | |
| end | |
| end | |
| end | |
| function DevDock:SnapToEdge(direction) | |
| local mover = self:ResolveId("idMover") | |
| direction = direction or mover.last_delta or point20 | |
| local origin = self.box:Center() | |
| local sx, sy = self.parent.box:sizexyz() | |
| local target | |
| if direction == point20 then | |
| local projections = { | |
| ClosestPtPointSegment(point(0, 0), point(0, sy), origin), | |
| ClosestPtPointSegment(point(0, sy), point(sx, sy), origin), | |
| ClosestPtPointSegment(point(sx, sy), point(sx, 0), origin), | |
| ClosestPtPointSegment(point(sx, 0), point(0, 0), origin), | |
| nil, | |
| } | |
| local min_dist = Max(sx, sy) | |
| for _, p in ipairs(projections) do | |
| local dist = p:SetInvalidZ():Dist2D(origin) | |
| if dist < min_dist then | |
| min_dist = dist | |
| target = p | |
| end | |
| end | |
| else | |
| target = | |
| IntersectRayWithSegment2D(origin, direction, point(0, 0), point(0, sy)) or | |
| IntersectRayWithSegment2D(origin, direction, point(0, sy), point(sx, sy)) or | |
| IntersectRayWithSegment2D(origin, direction, point(sx, sy), point(sx, 0)) or | |
| IntersectRayWithSegment2D(origin, direction, point(sx, 0), point(0, 0)) | |
| end | |
| if not target then | |
| return | |
| end | |
| if target:x() == 0 then self:SetDockSide("left") end | |
| if target:y() == 0 then self:SetDockSide("top") end | |
| if target:x() == sx then self:SetDockSide("right") end | |
| if target:y() == sy then self:SetDockSide("bottom") end | |
| target = target - self.box:size() / 2 | |
| table.set(LocalStorage, "DevDock", "DockPos", target) | |
| local dist = target:Dist2D(origin) | |
| local interpolation = { | |
| id = "SnapToEdge", | |
| type = const.intRect, | |
| originalRect = sizebox(target, self.box:size()), | |
| targetRect = self.box, | |
| duration = direction ~= point20 and Max(500, dist / direction:Len()) or 500, | |
| flags = const.intfInverse, | |
| } | |
| mover:ApplyOffsetToTarget(self, target) | |
| self:AddInterpolation(interpolation) | |
| if not self:IsThreadRunning("UpdatePopupAnchor") then | |
| self:CreateThread("UpdatePopupAnchor", function(self) | |
| Sleep(interpolation.duration) | |
| self:UpdatePopupAnchor() | |
| self.popup:SetVisible(mover.popup_visible_before_drag) | |
| mover.popup_visible_before_drag = nil | |
| end, self) | |
| end | |
| end | |
| local arrow_props = { | |
| left = { text = ">", h = "right", v = "center" }, | |
| right = { text = "<", h = "left", v = "center" }, | |
| top = { text = "v", h = "center", v = "bottom" }, | |
| bottom = { text = "^", h = "center", v = "top" }, | |
| } | |
| function DevDock:SetDockSide(side) | |
| self.dock_side = side | |
| local arrow = self:ResolveId("idArrow") | |
| arrow:SetText(arrow_props[side].text) | |
| arrow:SetTextHAlign(arrow_props[side].h) | |
| arrow:SetTextVAlign(arrow_props[side].v) | |
| table.set(LocalStorage, "DevDock", "DockSide", side) | |
| end | |
| local popup_props = { | |
| left = { anchor = "right" }, | |
| right = { anchor = "left" }, | |
| top = { anchor = "bottom" }, | |
| bottom = { anchor = "top" }, | |
| } | |
| function DevDock:UpdatePopupAnchor() | |
| local side = self.dock_side | |
| local popup = self.popup | |
| local anchor = table.get(popup_props, side, "anchor") | |
| popup:SetAnchorType(anchor or "smart") | |
| popup:SetAnchor(self.box) | |
| popup:InvalidateLayout() | |
| end | |
| function DevDock:MouseInWindow(pt, for_target) | |
| if not for_target then | |
| return true | |
| end | |
| return XDialog.MouseInWindow(self, pt) | |
| end | |
| function DevDock:GetMouseTarget(pt) | |
| local function get_target(win, pt) | |
| for i,child in ipairs(win) do | |
| local target, cursor = get_target(child, pt) | |
| if target then | |
| return target, cursor | |
| end | |
| end | |
| if win:MouseInWindow(pt, true) then | |
| if IsKindOf(win, "XPopup") then | |
| return win:GetMouseTarget(pt) | |
| elseif IsKindOf(win, "DevDock") then | |
| return XWindow.GetMouseTarget(win, pt) | |
| end | |
| end | |
| end | |
| return get_target(self, pt) | |
| end | |
| function DevDock:Close() | |
| return XDialog.Close(self) | |
| end | |
| ---- | |
| DefineClass.DevDockXTextButton = { | |
| __parents = { "XTextButton" }, | |
| BorderWidth = 1, | |
| BorderColor = RGBA(0, 0, 0, 0), | |
| RolloverTemplate = "GedPropRollover" | |
| } | |
| DefineClass.DevDockXToggleButton = { | |
| __parents = { "XToggleButton" }, | |
| BorderWidth = 1, | |
| BorderColor = RGBA(0, 0, 0, 0), | |
| ToggledBorderColor = RGB(0, 0, 0), | |
| RolloverTemplate = "GedPropRollover" | |
| } | |
| DefineClass.DevDockPopup = { | |
| __parents = { "XPopup" }, | |
| IdNode = true, | |
| HandleMouse = true, | |
| LayoutMethod = "VList", | |
| BorderWidth = 1, | |
| BorderColor = black, | |
| autohide = true, | |
| pinned = false, | |
| dock = false, | |
| plugins = false, | |
| } | |
| function DevDockPopup:Init() | |
| self.pinned = table.get(LocalStorage, "DevDock", "Pinned") | |
| self:CreateThread("AutoHideThread", function(self) | |
| local desktop = self.desktop | |
| while self.window_state ~= "destroying" do | |
| Sleep(250) | |
| if not self.pinned and self.autohide and self:GetVisible() then | |
| local target = desktop.last_mouse_target or desktop.mouse_capture | |
| if not GetParentOfKind(target, "DevDock") then | |
| self:SetVisible(false) | |
| end | |
| end | |
| end | |
| end, self) | |
| local toolbar = XToolBar:new({ | |
| Toolbar = "toolbar", | |
| Show = "icon", | |
| ButtonTemplate = "DevDockXTextButton", | |
| ToggleButtonTemplate = "DevDockXToggleButton", | |
| FocusOnClick = false, | |
| AutoHide = false, | |
| MaxHeight = 16, | |
| HAlign = "right", | |
| }, self) | |
| self:InitControls() | |
| self:InitPlugins() | |
| end | |
| function DevDockPopup:InitControls(container) | |
| XAction:new({ | |
| ActionSortKey = "100", | |
| ActionId = "Preferences", | |
| ActionIcon = "CommonAssets/UI/Icons/spanner", | |
| ActionToolbar = "toolbar", | |
| OnAction = function(self, host, source, ...) | |
| host.popup:OpenPreferences(source) | |
| end, | |
| }, self) | |
| XAction:new({ | |
| ActionSortKey = "101", | |
| ActionId = "Pin", | |
| ActionIcon = "CommonAssets/UI/Icons/pin", | |
| ActionToolbar = "toolbar", | |
| ActionToggle = true, | |
| ActionToggled = function(self, host) | |
| return host.popup.pinned | |
| end, | |
| OnAction = function(self, host, source, ...) | |
| local new_pinned = not host.popup.pinned | |
| host.popup.pinned = new_pinned | |
| table.set(LocalStorage, "DevDock", "Pinned", new_pinned) | |
| end, | |
| ActionState = function(self, host) | |
| if not host.popup.autohide then | |
| return "hidden" | |
| end | |
| end, | |
| }, self) | |
| end | |
| function DevDockPopup:InitPlugins() | |
| self.plugins = {} | |
| self:UpdatePlugins() | |
| end | |
| function DevDockPopup:UpdatePlugins() | |
| local classes = ClassDescendantsList("DevDockPlugin") | |
| for i, name in ipairs(classes) do | |
| local plugin_class = g_Classes[name] | |
| local is_valid = table.get(LocalStorage, "DevDock", "Plugins", name) and plugin_class:IsValid() | |
| if not self.plugins[name] and is_valid then | |
| local plugin = plugin_class:new({ | |
| dock = self.dock, | |
| }, self) | |
| self.plugins[name] = plugin | |
| if self.window_state ~= "new" then | |
| plugin:Open() | |
| end | |
| elseif self.plugins[name] and not is_valid then | |
| self.plugins[name]:Close() | |
| self.plugins[name] = nil | |
| end | |
| end | |
| if not next(self.plugins) then | |
| local text = XLabel:new({}, self) | |
| text:SetText(next(classes) and "Check the wrench icon." or "Nothing to see here...") | |
| if self.window_state ~= "new" then | |
| text:Open() | |
| end | |
| self.plugins["__empty"] = text | |
| elseif self.plugins["__empty"] then | |
| self.plugins["__empty"]:Close() | |
| self.plugins["__empty"] = nil | |
| end | |
| end | |
| function DevDockPopup:OpenPreferences(anchor) | |
| local list = XPopupList:new({ | |
| Anchor = anchor.box, | |
| AnchorType = "smart", | |
| ZOrder = 10, | |
| }, self) | |
| local classes = ClassDescendantsList("DevDockPlugin") | |
| for i, name in ipairs(classes) do | |
| local plugin_class = g_Classes[name] | |
| local checkbox = XCheckButton:new({}, list) | |
| checkbox:SetCheck(table.get(LocalStorage, "DevDock", "Plugins", name)) | |
| local display_name = name | |
| if display_name:starts_with("DevDock") then | |
| display_name = display_name:sub(#"DevDock" + 1) | |
| end | |
| if display_name:ends_with("Plugin") then | |
| display_name = display_name:sub(1, -#"Plugin" - 1) | |
| end | |
| checkbox:SetText(display_name) | |
| function checkbox:OnChange(check) | |
| table.set(LocalStorage, "DevDock", "Plugins", name, check) | |
| GetParentOfKind(self, "DevDockPopup"):UpdatePlugins() | |
| end | |
| end | |
| if not next(classes) then | |
| local text = XLabel:new({}, list) | |
| text:SetText("No plugins available.") | |
| end | |
| list:Open() | |
| end | |
| function DevDockPopup:SetAutohide(autohide) | |
| self.autohide = autohide | |
| GetActionsHost(self, true):UpdateActionViews() | |
| end | |
| function DevDockPopup:SetVisible(visible, instant, callback) | |
| table.set(LocalStorage, "DevDock", "Popup", not not visible) | |
| return XPopup.SetVisible(self, visible, instant, callback) | |
| end | |
| function DevDockPopup:OnKillFocus(new_focus) | |
| return XWindow.OnKillFocus(self, new_focus) | |
| end | |
| function DevDockPopup:OnMouseButtonDown(pt, button) | |
| return XWindow.OnMouseButtonDown(self, pt, button) | |
| end | |
| ---- | |
| DefineClass.DevDockPlugin = { | |
| __parents = { "XWindow", "XActionsHost" }, | |
| HostInParent = true, | |
| dock = false, | |
| } | |
| function DevDockPlugin.IsValid() | |
| return true | |
| end | |
| function GetDevDockPlugin(class) | |
| return table.get(GetDialog("DevDock"), "popup", "plugins", class) | |
| end | |
| ---- | |
| function OnMsg.DesktopCreated() | |
| if Platform.ged then return end | |
| if not config.DevDock then return end | |
| OpenDialog("DevDock", terminal.desktop, nil, "auto") | |
| end | |
| local function update_plugins() | |
| local dlg = GetDialog("DevDock") | |
| if not dlg then return end | |
| dlg.popup:UpdatePlugins() | |
| end | |
| OnMsg.ChangeMapDone = update_plugins | |
| OnMsg.PostLoadGame = update_plugins | |
| OnMsg.InGameInterfaceCreated = update_plugins | |
| function DevDockOpen() | |
| OpenDialog("DevDock", terminal.desktop) | |
| end | |
| function DevDockClose() | |
| CloseDialog("DevDock") | |
| end | |
| ---- | |
| --[[ | |
| DefineClass.HelloWorldPlugin = { | |
| __parents = { "DevDockPlugin" }, | |
| Init = function(self) | |
| local text = XLabel:new({}, self) | |
| text:SetText("Hello world!") | |
| XAction:new({ | |
| ActionId = "HelloWorld", | |
| ActionToolbar = "toolbar", | |
| ActionIcon = "CommonAssets/UI/Icons/pi", | |
| OnAction = function(self, host, source, ...) | |
| host:Flash() | |
| end, | |
| }, self) | |
| XAction:new({ | |
| ActionId = "HelloWorld2", | |
| ActionToolbar = "toolbar", | |
| ActionIcon = "CommonAssets/UI/Icons/activity health lifeline medical pulse", | |
| OnAction = function(self, host, source, ...) | |
| host.popup:SetVisible(false) | |
| host:Notify() | |
| end, | |
| }, self) | |
| end, | |
| } | |
| ]] | |