From f11bd3ed25028195d66f69d0836368f1c5d9766a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Fri, 26 Sep 2014 12:23:53 +0300 Subject: [PATCH] extensions/__menu: Figuring out how this thing should work --- extensions/__menu/init.lua | 117 ++++++--- extensions/__menu/res/boot_style.png | Bin 0 -> 9502 bytes extensions/__menu/res/boot_style.xml | 342 +++++++++++++++++++++++++++ 3 files changed, 427 insertions(+), 32 deletions(-) create mode 100644 extensions/__menu/res/boot_style.png create mode 100644 extensions/__menu/res/boot_style.xml diff --git a/extensions/__menu/init.lua b/extensions/__menu/init.lua index 10f4c33..c9090d2 100644 --- a/extensions/__menu/init.lua +++ b/extensions/__menu/init.lua @@ -7,45 +7,98 @@ local dump = buildat.dump local M = {safe = nil} log:info("extension/__menu/init.lua: Loading") +local ui_stack_name_i = 1 -- For generating a unique name for each stack +local function UIStack(root) + local self = {} + self.root = root + self.stack_name = "ui_stack_"..ui_stack_name_i + ui_stack_name_i = ui_stack_name_i + 1 + self.element_name_i = 1 + self.stack = {} + function self:push() + if #self.stack >= 1 then + local top = self.stack[#self.stack] + top:SetVisible(false) + end + local element_name = self.stack_name.."_"..self.element_name_i + local element = self.root:CreateChild("UIElement") + element:SetName(element_name) + element:SetStyleAuto() + element:SetLayout(LM_HORIZONTAL, 0, IntRect(0, 0, 0, 0)) + element:SetAlignment(HA_CENTER, VA_CENTER) + self.element_name_i = self.element_name_i + 1 + table.insert(self.stack, element) + return element + end + function self:pop() + local top = table.remove(self.stack) + self.root:RemoveChild(top) + if #self.stack >= 1 then + local top = self.stack[#self.stack] + top:SetVisible(true) + end + end + return self +end + +local ui_stack = UIStack(ui.root) + +local function launch_connect_to_server() +end + function M.boot() - local style = cache:GetResource("XMLFile", "UI/DefaultStyle.xml") + local style = cache:GetResource("XMLFile", "__menu/res/boot_style.xml") ui.root.defaultStyle = style - window = Window:new() - window:SetStyleAuto() - ui.root:AddChild(window) - window:SetMinSize(384, 192) - window:SetLayout(LM_HORIZONTAL, 6, IntRect(6, 6, 6, 6)) - window:SetAlignment(HA_CENTER, VA_CENTER) - window:SetName("Window") - - local checkBox = CheckBox:new() - checkBox:SetName("CheckBox") - checkBox:SetStyleAuto() - window:AddChild(checkBox) + local root = ui_stack:push() - local button = Button:new() + local test_element = root:CreateChild("Sprite") + test_element:SetTexture( + cache:GetResource("Texture2D", "__menu/res/icon_network.png")) + test_element.color = Color(.2, .2, .2) + test_element:SetFixedSize(400, 400) + + local root = ui_stack:push() + + local layout = root:CreateChild("Window") + layout:SetStyleAuto() + layout:SetName("Layout") + layout:SetLayout(LM_HORIZONTAL, 20, IntRect(0, 0, 0, 0)) + layout:SetAlignment(HA_CENTER, VA_CENTER) + + local button = layout:CreateChild("Button") button:SetStyleAuto() button:SetName("Button") - button.Height = 200 - button.Width = 200 - window:AddChild(button) - --[[local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf") - local buttonText = button:CreateChild("Text") - buttonText:SetAlignment(magic.HA_CENTER, magic.HA_CENTER) - buttonText.text = "Button Text" - buttonText:SetFont(font)]] - local buttonImage = button:CreateChild("Sprite") - --buttonImage:SetTexture(cache:GetResource("Texture2D", "Textures/LogoLarge.png")) - buttonImage:SetTexture(cache:GetResource("Texture2D", "__menu/res/icon_network.png")) - buttonImage:SetAlignment(magic.HA_CENTER, magic.HA_CENTER) - buttonImage:SetSize(200, 200) + button:SetLayout(LM_VERTICAL, 10, IntRect(0, 0, 0, 0)) + local button_image = button:CreateChild("Sprite") + button_image:SetName("ButtonImage") + button_image:SetTexture( + cache:GetResource("Texture2D", "__menu/res/icon_network.png")) + button_image.color = Color(.3, .3, .3) + button_image:SetFixedSize(200, 200) + local button_text = button:CreateChild("Text") + button_text:SetName("ButtonText") + button_text:SetStyleAuto() + button_text.text = "Connect to server" + button_text.color = Color(.3, .3, .3) + button_text:SetAlignment(HA_CENTER, VA_CENTER) + button_text:SetTextAlignment(HA_CENTER) - local lineEdit = LineEdit:new() - lineEdit:SetStyleAuto() - lineEdit:SetName("LineEdit") - lineEdit.minHeight = 24 - window:AddChild(lineEdit) + magic.SubscribeToEvent(button, "HoverBegin", + function(self, event_type, event_data) + self:GetChild("ButtonImage").color = Color(1, 1, 1) + self:GetChild("ButtonText").color = Color(1, 1, 1) + end) + magic.SubscribeToEvent(button, "HoverEnd", + function(self, event_type, event_data) + self:GetChild("ButtonImage").color = Color(.3, .3, .3) + self:GetChild("ButtonText").color = Color(.3, .3, .3) + end) + magic.SubscribeToEvent(button, "Released", + function(self, event_type, event_data) + log:info("Button clicked: \"Connect to server\"") + launch_connect_to_server() + end) magic.SubscribeToEvent("KeyDown", function(event_type, event_data) local key = event_data:GetInt("Key") diff --git a/extensions/__menu/res/boot_style.png b/extensions/__menu/res/boot_style.png new file mode 100644 index 0000000000000000000000000000000000000000..c9d53c1d3b968c4241d8e5d4ae2969452da33a6a GIT binary patch literal 9502 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 R@J%K_TT@q~`o2xX{{!Er{8az| literal 0 HcmV?d00001 diff --git a/extensions/__menu/res/boot_style.xml b/extensions/__menu/res/boot_style.xml new file mode 100644 index 0000000..fc3ad42 --- /dev/null +++ b/extensions/__menu/res/boot_style.xml @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +