From 8e7cf014928fb8d333b75319bb671d398fca6be7 Mon Sep 17 00:00:00 2001 From: yvt Date: Sat, 30 Dec 2017 22:06:38 +0900 Subject: [PATCH] Refactor AngelScript code Split huge source files --- Resources/Scripts/Gui/Client/ChatLogWindow.as | 204 ++ Resources/Scripts/Gui/Client/ChatSayWindow.as | 318 +++ Resources/Scripts/Gui/Client/ClientUI.as | 164 ++ .../Scripts/Gui/Client/FieldWithHistory.as | 104 + Resources/Scripts/Gui/Client/Menu.as | 108 + Resources/Scripts/Gui/ClientUI.as | 799 ------- Resources/Scripts/Gui/Gui.as | 19 +- Resources/Scripts/Gui/MainScreen.as | 838 -------- .../{Flags.as => MainScreen/CountryFlags.as} | 0 .../{ => MainScreen}/CreateProfileScreen.as | 0 Resources/Scripts/Gui/MainScreen/MainMenu.as | 519 +++++ .../Scripts/Gui/MainScreen/MainScreenUI.as | 195 ++ .../Scripts/Gui/MainScreen/ServerList.as | 177 ++ Resources/Scripts/Gui/StartupScreen.as | 1906 ----------------- .../Gui/StartupScreen/ConfigViewFramework.as | 754 +++++++ .../Gui/StartupScreen/ConfigViewTabs.as | 868 ++++++++ .../Scripts/Gui/StartupScreen/MainMenu.as | 226 ++ .../Gui/StartupScreen/StartupScreenUI.as | 131 ++ .../{ => StartupScreen}/UpdateCheckView.as | 4 +- Resources/Scripts/Gui/UIControls.as | 1776 --------------- Resources/Scripts/Gui/UIFramework/Button.as | 368 ++++ .../Gui/{ => UIFramework}/DropDownList.as | 0 Resources/Scripts/Gui/UIFramework/Field.as | 504 +++++ Resources/Scripts/Gui/UIFramework/Label.as | 60 + Resources/Scripts/Gui/UIFramework/ListView.as | 212 ++ .../Scripts/Gui/UIFramework/ScrollBar.as | 362 ++++ Resources/Scripts/Gui/UIFramework/Slider.as | 208 ++ Resources/Scripts/Gui/UIFramework/TabStrip.as | 115 + .../Scripts/Gui/UIFramework/TextViewer.as | 138 ++ .../Scripts/Gui/UIFramework/UIControls.as | 28 + .../Gui/{ => UIFramework}/UIFramework.as | 0 31 files changed, 5774 insertions(+), 5331 deletions(-) create mode 100644 Resources/Scripts/Gui/Client/ChatLogWindow.as create mode 100644 Resources/Scripts/Gui/Client/ChatSayWindow.as create mode 100644 Resources/Scripts/Gui/Client/ClientUI.as create mode 100644 Resources/Scripts/Gui/Client/FieldWithHistory.as create mode 100644 Resources/Scripts/Gui/Client/Menu.as delete mode 100644 Resources/Scripts/Gui/ClientUI.as delete mode 100644 Resources/Scripts/Gui/MainScreen.as rename Resources/Scripts/Gui/{Flags.as => MainScreen/CountryFlags.as} (100%) rename Resources/Scripts/Gui/{ => MainScreen}/CreateProfileScreen.as (100%) create mode 100644 Resources/Scripts/Gui/MainScreen/MainMenu.as create mode 100644 Resources/Scripts/Gui/MainScreen/MainScreenUI.as create mode 100644 Resources/Scripts/Gui/MainScreen/ServerList.as delete mode 100644 Resources/Scripts/Gui/StartupScreen.as create mode 100644 Resources/Scripts/Gui/StartupScreen/ConfigViewFramework.as create mode 100644 Resources/Scripts/Gui/StartupScreen/ConfigViewTabs.as create mode 100644 Resources/Scripts/Gui/StartupScreen/MainMenu.as create mode 100644 Resources/Scripts/Gui/StartupScreen/StartupScreenUI.as rename Resources/Scripts/Gui/{ => StartupScreen}/UpdateCheckView.as (99%) delete mode 100644 Resources/Scripts/Gui/UIControls.as create mode 100644 Resources/Scripts/Gui/UIFramework/Button.as rename Resources/Scripts/Gui/{ => UIFramework}/DropDownList.as (100%) create mode 100644 Resources/Scripts/Gui/UIFramework/Field.as create mode 100644 Resources/Scripts/Gui/UIFramework/Label.as create mode 100644 Resources/Scripts/Gui/UIFramework/ListView.as create mode 100644 Resources/Scripts/Gui/UIFramework/ScrollBar.as create mode 100644 Resources/Scripts/Gui/UIFramework/Slider.as create mode 100644 Resources/Scripts/Gui/UIFramework/TabStrip.as create mode 100644 Resources/Scripts/Gui/UIFramework/TextViewer.as create mode 100644 Resources/Scripts/Gui/UIFramework/UIControls.as rename Resources/Scripts/Gui/{ => UIFramework}/UIFramework.as (100%) diff --git a/Resources/Scripts/Gui/Client/ChatLogWindow.as b/Resources/Scripts/Gui/Client/ChatLogWindow.as new file mode 100644 index 00000000..da794d56 --- /dev/null +++ b/Resources/Scripts/Gui/Client/ChatLogWindow.as @@ -0,0 +1,204 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ +#include "ChatSayWindow.as" + +namespace spades { + + class ChatLogSayWindow: ClientChatWindow { + ChatLogWindow@ owner; + + ChatLogSayWindow(ChatLogWindow@ own, bool isTeamChat) { + super(own.ui, isTeamChat); + @owner = own; + } + + void Close() { + owner.SayWindowClosed(); + @this.Parent = null; + } + } + + class ChatLogWindow: spades::ui::UIElement { + + float contentsTop, contentsHeight; + + ClientUI@ ui; + private ClientUIHelper@ helper; + + private spades::ui::TextViewer@ viewer; + ChatLogSayWindow@ sayWindow; + + private spades::ui::UIElement@ sayButton1; + private spades::ui::UIElement@ sayButton2; + + ChatLogWindow(ClientUI@ ui) { + super(ui.manager); + @this.ui = ui; + @this.helper = ui.helper; + + @Font = Manager.RootElement.Font; + this.Bounds = Manager.RootElement.Bounds; + + float contentsWidth = 700.f; + float contentsLeft = (Manager.Renderer.ScreenWidth - contentsWidth) * 0.5f; + contentsHeight = Manager.Renderer.ScreenHeight - 200.f; + contentsTop = (Manager.Renderer.ScreenHeight - contentsHeight - 106.f) * 0.5f; + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.4f); + label.Bounds = Bounds; + AddChild(label); + } + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.8f); + label.Bounds = AABB2(0.f, contentsTop - 13.f, Size.x, contentsHeight + 27.f); + AddChild(label); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Close"); + button.Bounds = AABB2( + contentsLeft + contentsWidth - 150.f, + contentsTop + contentsHeight - 30.f + , 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnOkPressed); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Say Global"); + button.Bounds = AABB2( + contentsLeft, + contentsTop + contentsHeight - 30.f + , 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnGlobalChat); + AddChild(button); + @this.sayButton1 = button; + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Say Team"); + button.Bounds = AABB2( + contentsLeft + 155.f, + contentsTop + contentsHeight - 30.f + , 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnTeamChat); + AddChild(button); + @this.sayButton2 = button; + } + { + spades::ui::TextViewer viewer(Manager); + AddChild(viewer); + viewer.Bounds = AABB2(contentsLeft, contentsTop, contentsWidth, contentsHeight - 40.f); + @this.viewer = viewer; + } + } + + void ScrollToEnd() { + viewer.Layout(); + viewer.ScrollToEnd(); + } + + void Close() { + @ui.ActiveUI = null; + } + + void SayWindowClosed() { + @sayWindow = null; + sayButton1.Enable = true; + sayButton2.Enable = true; + } + + private void OnOkPressed(spades::ui::UIElement@ sender) { + Close(); + } + + private void OnTeamChat(spades::ui::UIElement@ sender) { + if(sayWindow !is null) { + sayWindow.IsTeamChat = true; + return; + } + sayButton1.Enable = false; + sayButton2.Enable = false; + ChatLogSayWindow wnd(this, true); + AddChild(wnd); + wnd.Bounds = this.Bounds; + @this.sayWindow = wnd; + @Manager.ActiveElement = wnd.field; + } + + private void OnGlobalChat(spades::ui::UIElement@ sender) { + if(sayWindow !is null) { + sayWindow.IsTeamChat = false; + return; + } + sayButton1.Enable = false; + sayButton2.Enable = false; + ChatLogSayWindow wnd(this, false); + AddChild(wnd); + wnd.Bounds = this.Bounds; + @this.sayWindow = wnd; + @Manager.ActiveElement = wnd.field; + } + + void HotKey(string key) { + if(sayWindow !is null) { + UIElement::HotKey(key); + return; + } + if(IsEnabled and (key == "Escape")) { + Close(); + } else if(IsEnabled and (key == "y")) { + OnTeamChat(this); + } else if(IsEnabled and (key == "t")) { + OnGlobalChat(this); + } else { + UIElement::HotKey(key); + } + } + + void Record(string text, Vector4 color) { + viewer.AddLine(text, this.IsVisible, color); + } + + void Render() { + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Renderer@ r = Manager.Renderer; + Image@ img = r.RegisterImage("Gfx/White.tga"); + + r.ColorNP = Vector4(1, 1, 1, 0.08f); + r.DrawImage(img, + AABB2(pos.x, pos.y + contentsTop - 15.f, size.x, 1.f)); + r.DrawImage(img, + AABB2(pos.x, pos.y + contentsTop + contentsHeight + 15.f, size.x, 1.f)); + r.ColorNP = Vector4(1, 1, 1, 0.2f); + r.DrawImage(img, + AABB2(pos.x, pos.y + contentsTop - 14.f, size.x, 1.f)); + r.DrawImage(img, + AABB2(pos.x, pos.y + contentsTop + contentsHeight + 14.f, size.x, 1.f)); + + UIElement::Render(); + } + + } + +} diff --git a/Resources/Scripts/Gui/Client/ChatSayWindow.as b/Resources/Scripts/Gui/Client/ChatSayWindow.as new file mode 100644 index 00000000..481c2d5c --- /dev/null +++ b/Resources/Scripts/Gui/Client/ChatSayWindow.as @@ -0,0 +1,318 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ +#include "FieldWithHistory.as" + +namespace spades { + + uint StringCommonPrefixLength(string a, string b) { + for(uint i = 0, ln = Min(a.length, b.length); i < ln; i++) { + if(ToLower(a[i]) != ToLower(b[i])) return i; + } + return Min(a.length, b.length); + } + + /** Shows cvar's current value when user types something like "/cg_foobar" */ + class CommandFieldConfigValueView: spades::ui::UIElement { + string[]@ configNames; + string[] configValues; + CommandFieldConfigValueView(spades::ui::UIManager@ manager, string[] configNames) { + super(manager); + for(uint i = 0, len = configNames.length; i < len; i++) { + configValues.insertLast(ConfigItem(configNames[i]).StringValue); + } + @this.configNames = configNames; + } + void Render() { + float maxNameLen = 0.f; + float maxValueLen = 20.f; + Font@ font = this.Font; + Renderer@ renderer = this.Manager.Renderer; + float rowHeight = 25.f; + + for(uint i = 0, len = configNames.length; i < len; i++) { + maxNameLen = Max(maxNameLen, font.Measure(configNames[i]).x); + maxValueLen = Max(maxValueLen, font.Measure(configValues[i]).x); + } + Vector2 pos = this.ScreenPosition; + pos.y -= float(configNames.length) * rowHeight + 10.f; + + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.5f); + renderer.DrawImage(null, + AABB2(pos.x, pos.y, maxNameLen + maxValueLen + 20.f, + float(configNames.length) * rowHeight + 10.f)); + + for(uint i = 0, len = configNames.length; i < len; i++) { + font.DrawShadow(configNames[i], + pos + Vector2(5.f, 8.f + float(i) * rowHeight), + 1.f, Vector4(1,1,1,0.7), Vector4(0,0,0,0.3f)); + font.DrawShadow(configValues[i], + pos + Vector2(15.f + maxNameLen, 8.f + float(i) * rowHeight), + 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.4f)); + } + + } + } + + class CommandField: FieldWithHistory { + CommandFieldConfigValueView@ valueView; + + CommandField(spades::ui::UIManager@ manager, array@ history) { + super(manager, history); + + } + + void OnChanged() { + FieldWithHistory::OnChanged(); + + if(valueView !is null) { + @valueView.Parent = null; + } + if(Text.substr(0, 1) == "/" && + Text.substr(1, 1) != " ") { + int whitespace = Text.findFirst(" "); + if(whitespace < 0) { + whitespace = int(Text.length); + } + + string input = Text.substr(1, whitespace - 1); + if(input.length >= 2) { + string[]@ names = GetAllConfigNames(); + string[] filteredNames; + for(uint i = 0, len = names.length; i < len; i++) { + if ( + StringCommonPrefixLength(input, names[i]) == input.length && + !ConfigItem(names[i]).IsUnknown + ) { + filteredNames.insertLast(names[i]); + if(filteredNames.length >= 8) { + // too many + break; + } + } + } + if(filteredNames.length > 0) { + @valueView = CommandFieldConfigValueView(this.Manager, filteredNames); + valueView.Bounds = AABB2(0.f, -15.f, 0.f, 0.f); + @valueView.Parent = this; + } + } + } + } + + void KeyDown(string key) { + if(key == "Tab") { + if(SelectionLength == 0 && + SelectionStart == int(Text.length) && + Text.substr(0, 1) == "/" && + Text.findFirst(" ") < 0) { + // config variable auto completion + string input = Text.substr(1); + string[]@ names = GetAllConfigNames(); + string commonPart; + bool foundOne = false; + for(uint i = 0, len = names.length; i < len; i++) { + if ( + StringCommonPrefixLength(input, names[i]) == input.length && + !ConfigItem(names[i]).IsUnknown + ) { + if(!foundOne) { + commonPart = names[i]; + foundOne = true; + } + + uint commonLen = StringCommonPrefixLength(commonPart, names[i]); + commonPart = commonPart.substr(0, commonLen); + } + } + + if(commonPart.length > input.length) { + Text = "/" + commonPart; + Select(Text.length, 0); + } + + } + }else{ + FieldWithHistory::KeyDown(key); + } + } + } + + class ClientChatWindow: spades::ui::UIElement { + private ClientUI@ ui; + private ClientUIHelper@ helper; + + CommandField@ field; + spades::ui::Button@ sayButton; + spades::ui::SimpleButton@ teamButton; + spades::ui::SimpleButton@ globalButton; + + bool isTeamChat; + + ClientChatWindow(ClientUI@ ui, bool isTeamChat) { + super(ui.manager); + @this.ui = ui; + @this.helper = ui.helper; + this.isTeamChat = isTeamChat; + + float winW = Manager.Renderer.ScreenWidth * 0.7f, winH = 66.f; + float winX = (Manager.Renderer.ScreenWidth - winW) * 0.5f; + float winY = (Manager.Renderer.ScreenHeight - winH) - 20.f; + /* + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.5f); + label.Bounds = Bounds; + AddChild(label); + }*/ + + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.5f); + label.Bounds = AABB2(winX - 8.f, winY - 8.f, winW + 16.f, winH + 16.f); + AddChild(label); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Say"); + button.Bounds = AABB2(winX + winW - 244.f, winY + 36.f, 120.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnSay); + AddChild(button); + @sayButton = button; + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Cancel"); + button.Bounds = AABB2(winX + winW - 120.f, winY + 36.f, 120.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnCancel); + AddChild(button); + } + { + @field = CommandField(Manager, ui.chatHistory); + field.Bounds = AABB2(winX, winY, winW, 30.f); + field.Placeholder = _Tr("Client", "Chat Text"); + @field.Changed = spades::ui::EventHandler(this.OnFieldChanged); + AddChild(field); + } + { + @globalButton = spades::ui::SimpleButton(Manager); + globalButton.Toggle = true; + globalButton.Toggled = isTeamChat == false; + globalButton.Caption = _Tr("Client", "Global"); + globalButton.Bounds = AABB2(winX, winY + 36.f, 70.f, 30.f); + @globalButton.Activated = spades::ui::EventHandler(this.OnSetGlobal); + AddChild(globalButton); + } + { + @teamButton = spades::ui::SimpleButton(Manager); + teamButton.Toggle = true; + teamButton.Toggled = isTeamChat == true; + teamButton.Caption = _Tr("Client", "Team"); + teamButton.Bounds = AABB2(winX + 70.f, winY + 36.f, 70.f, 30.f); + @teamButton.Activated = spades::ui::EventHandler(this.OnSetTeam); + AddChild(teamButton); + } + } + + void UpdateState() { + sayButton.Enable = field.Text.length > 0; + } + + bool IsTeamChat { + get final { return isTeamChat; } + set { + if(isTeamChat == value) return; + isTeamChat = value; + teamButton.Toggled = isTeamChat; + globalButton.Toggled = not isTeamChat; + UpdateState(); + } + } + + private void OnSetGlobal(spades::ui::UIElement@ sender) { + IsTeamChat = false; + } + private void OnSetTeam(spades::ui::UIElement@ sender) { + IsTeamChat = true; + } + + private void OnFieldChanged(spades::ui::UIElement@ sender) { + UpdateState(); + } + + private void Close() { + @ui.ActiveUI = null; + } + + private void OnCancel(spades::ui::UIElement@ sender) { + field.Cancelled(); + Close(); + } + + private bool CheckAndSetConfigVariable() { + string text = field.Text; + if(text.substr(0, 1) != "/") return false; + int idx = text.findFirst(" "); + if(idx < 2) return false; + + // find variable + string varname = text.substr(1, idx - 1); + string[] vars = GetAllConfigNames(); + + for(uint i = 0, len = vars.length; i < len; i++) { + if(vars[i].length == varname.length && + StringCommonPrefixLength(vars[i], varname) == vars[i].length) { + // match + string val = text.substr(idx + 1); + ConfigItem item(vars[i]); + item.StringValue = val; + return true; + } + } + + return false; + } + + private void OnSay(spades::ui::UIElement@ sender) { + field.CommandSent(); + if(!CheckAndSetConfigVariable()) { + if(isTeamChat) + ui.helper.SayTeam(field.Text); + else + ui.helper.SayGlobal(field.Text); + } + Close(); + } + + void HotKey(string key) { + if(IsEnabled and key == "Escape") { + OnCancel(this); + }else if(IsEnabled and key == "Enter") { + if(field.Text.length == 0) { + OnCancel(this); + }else{ + OnSay(this); + } + } else { + UIElement::HotKey(key); + } + } + } + +} diff --git a/Resources/Scripts/Gui/Client/ClientUI.as b/Resources/Scripts/Gui/Client/ClientUI.as new file mode 100644 index 00000000..d43b9b7e --- /dev/null +++ b/Resources/Scripts/Gui/Client/ClientUI.as @@ -0,0 +1,164 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ +#include "Menu.as" +#include "FieldWithHistory.as" +#include "ChatLogWindow.as" + +namespace spades { + + class ClientUI { + private Renderer@ renderer; + private AudioDevice@ audioDevice; + FontManager@ fontManager; + ClientUIHelper@ helper; + + spades::ui::UIManager@ manager; + spades::ui::UIElement@ activeUI; + + ChatLogWindow@ chatLogWindow; + + ClientMenu@ clientMenu; + + array chatHistory; + + bool shouldExit = false; + + private float time = -1.f; + + ClientUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, ClientUIHelper@ helper) { + @this.renderer = renderer; + @this.audioDevice = audioDevice; + @this.fontManager = fontManager; + @this.helper = helper; + + @manager = spades::ui::UIManager(renderer, audioDevice); + @manager.RootElement.Font = fontManager.GuiFont; + + @clientMenu = ClientMenu(this); + clientMenu.Bounds = manager.RootElement.Bounds; + + @chatLogWindow = ChatLogWindow(this); + } + + void MouseEvent(float x, float y) { + manager.MouseEvent(x, y); + } + + void WheelEvent(float x, float y) { + manager.WheelEvent(x, y); + } + + void KeyEvent(string key, bool down) { + manager.KeyEvent(key, down); + } + + void TextInputEvent(string text) { + manager.TextInputEvent(text); + } + + void TextEditingEvent(string text, int start, int len) { + manager.TextEditingEvent(text, start, len); + } + + bool AcceptsTextInput() { + return manager.AcceptsTextInput; + } + + AABB2 GetTextInputRect() { + return manager.TextInputRect; + } + + void RunFrame(float dt) { + if(time < 0.f) { + time = 0.f; + } + + manager.RunFrame(dt); + if(activeUI !is null){ + manager.Render(); + } + + time += Min(dt, 0.05f); + } + + void Closing() { + + } + + bool WantsClientToBeClosed() { + return shouldExit; + } + + bool NeedsInput() { + return activeUI !is null; + } + + void set_ActiveUI(spades::ui::UIElement@ value) { + if(activeUI !is null) { + manager.RootElement.RemoveChild(activeUI); + } + @activeUI = value; + if(activeUI !is null) { + activeUI.Bounds = manager.RootElement.Bounds; + manager.RootElement.AddChild(activeUI); + } + manager.KeyPanic(); + } + spades::ui::UIElement@ get_ActiveUI(){ + return activeUI; + } + + void EnterClientMenu() { + @ActiveUI = clientMenu; + } + + void EnterTeamChatWindow() { + ClientChatWindow wnd(this, true); + @ActiveUI = wnd; + @manager.ActiveElement = wnd.field; + } + void EnterGlobalChatWindow() { + ClientChatWindow wnd(this, false); + @ActiveUI = wnd; + @manager.ActiveElement = wnd.field; + } + void EnterCommandWindow() { + ClientChatWindow wnd(this, true); + wnd.field.Text = "/"; + wnd.field.Select(1, 0); + wnd.UpdateState(); + @ActiveUI = wnd; + @manager.ActiveElement = wnd.field; + } + void CloseUI() { + @ActiveUI = null; + } + + void RecordChatLog(string text, Vector4 color) { + chatLogWindow.Record(text, color); + } + } + + ClientUI@ CreateClientUI(Renderer@ renderer, AudioDevice@ audioDevice, + FontManager@ fontManager, ClientUIHelper@ helper) { + return ClientUI(renderer, audioDevice, fontManager, helper); + } + +} diff --git a/Resources/Scripts/Gui/Client/FieldWithHistory.as b/Resources/Scripts/Gui/Client/FieldWithHistory.as new file mode 100644 index 00000000..04a9bbd1 --- /dev/null +++ b/Resources/Scripts/Gui/Client/FieldWithHistory.as @@ -0,0 +1,104 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ +#include "../UIFramework/Field.as" + +namespace spades { + + class CommandHistoryItem { + string text; + int selStart; + int selEnd; + CommandHistoryItem() {} + CommandHistoryItem(string text, int selStart, int selEnd) { + this.text = text; + this.selStart = selStart; + this.selEnd = selEnd; + } + } + + // Field with bash-like history support + class FieldWithHistory: spades::ui::Field { + array@ cmdhistory; + CommandHistoryItem@ temporalLastHistory; + uint currentHistoryIndex; + + FieldWithHistory(spades::ui::UIManager@ manager, array@ history) { + super(manager); + + @this.cmdhistory = history; + currentHistoryIndex = history.length; + @temporalLastHistory = this.CommandHistoryItemRep; + } + + private CommandHistoryItem@ CommandHistoryItemRep { + get { + return CommandHistoryItem(this.Text, this.SelectionStart, this.SelectionEnd); + } + set { + this.Text = value.text; + this.Select(value.selStart, value.selEnd - value.selStart); + } + } + + private void OverwriteItem() { + if(currentHistoryIndex < cmdhistory.length) { + @cmdhistory[currentHistoryIndex] = this.CommandHistoryItemRep; + }else if(currentHistoryIndex == cmdhistory.length) { + @temporalLastHistory = this.CommandHistoryItemRep; + } + } + + private void LoadItem() { + if(currentHistoryIndex < cmdhistory.length) { + @this.CommandHistoryItemRep = cmdhistory[currentHistoryIndex]; + }else if(currentHistoryIndex == cmdhistory.length) { + @this.CommandHistoryItemRep = temporalLastHistory; + } + } + + void KeyDown(string key) { + if(key == "Up") { + if(currentHistoryIndex > 0) { + OverwriteItem(); + currentHistoryIndex--; + LoadItem(); + } + }else if(key == "Down") { + if(currentHistoryIndex < cmdhistory.length) { + OverwriteItem(); + currentHistoryIndex++; + LoadItem(); + } + }else{ + Field::KeyDown(key); + } + } + + void CommandSent() { + cmdhistory.insertLast(this.CommandHistoryItemRep); + currentHistoryIndex = cmdhistory.length - 1; + } + + void Cancelled() { + OverwriteItem(); + } + }; + +} diff --git a/Resources/Scripts/Gui/Client/Menu.as b/Resources/Scripts/Gui/Client/Menu.as new file mode 100644 index 00000000..d0bed603 --- /dev/null +++ b/Resources/Scripts/Gui/Client/Menu.as @@ -0,0 +1,108 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ +#include "ClientUI.as" + +namespace spades { + + class ClientMenu: spades::ui::UIElement { + private ClientUI@ ui; + private ClientUIHelper@ helper; + + ClientMenu(ClientUI@ ui) { + super(ui.manager); + @this.ui = ui; + @this.helper = ui.helper; + + float winW = 180.f, winH = 32.f * 4.f - 2.f; + float winX = (Manager.Renderer.ScreenWidth - winW) * 0.5f; + float winY = (Manager.Renderer.ScreenHeight - winH) * 0.5f; + + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.5f); + label.Bounds = AABB2(0.f, 0.f, + Manager.Renderer.ScreenWidth, Manager.Renderer.ScreenHeight); + AddChild(label); + } + + { + spades::ui::Label label(Manager); + label.BackgroundColor = Vector4(0, 0, 0, 0.5f); + label.Bounds = AABB2(winX - 8.f, winY - 8.f, winW + 16.f, winH + 16.f); + AddChild(label); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Back to Game"); + button.Bounds = AABB2(winX, winY, winW, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnBackToGame); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Chat Log"); + button.Bounds = AABB2(winX, winY + 32.f, winW, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnChatLog); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Setup"); + button.Bounds = AABB2(winX, winY + 64.f, winW, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnSetup); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("Client", "Disconnect"); + button.Bounds = AABB2(winX, winY + 96.f, winW, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnDisconnect); + AddChild(button); + } + } + + private void OnBackToGame(spades::ui::UIElement@ sender) { + @ui.ActiveUI = null; + } + private void OnSetup(spades::ui::UIElement@ sender) { + PreferenceViewOptions opt; + opt.GameActive = true; + + PreferenceView al(this, opt, ui.fontManager); + al.Run(); + } + private void OnChatLog(spades::ui::UIElement@ sender) { + @ui.ActiveUI = @ui.chatLogWindow; + ui.chatLogWindow.ScrollToEnd(); + } + private void OnDisconnect(spades::ui::UIElement@ sender) { + ui.shouldExit = true; + } + + void HotKey(string key) { + if(IsEnabled and key == "Escape") { + @ui.ActiveUI = null; + } else { + UIElement::HotKey(key); + } + } + } + +} diff --git a/Resources/Scripts/Gui/ClientUI.as b/Resources/Scripts/Gui/ClientUI.as deleted file mode 100644 index 339b9eb9..00000000 --- a/Resources/Scripts/Gui/ClientUI.as +++ /dev/null @@ -1,799 +0,0 @@ -/* - Copyright (c) 2013 yvt - - This file is part of OpenSpades. - - OpenSpades is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenSpades is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OpenSpades. If not, see . - - */ - - -namespace spades { - - class CommandHistoryItem { - string text; - int selStart; - int selEnd; - CommandHistoryItem() {} - CommandHistoryItem(string text, int selStart, int selEnd) { - this.text = text; - this.selStart = selStart; - this.selEnd = selEnd; - } - } - - class ClientUI { - private Renderer@ renderer; - private AudioDevice@ audioDevice; - FontManager@ fontManager; - ClientUIHelper@ helper; - - spades::ui::UIManager@ manager; - spades::ui::UIElement@ activeUI; - - ChatLogWindow@ chatLogWindow; - - ClientMenu@ clientMenu; - - array chatHistory; - - bool shouldExit = false; - - private float time = -1.f; - - ClientUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, ClientUIHelper@ helper) { - @this.renderer = renderer; - @this.audioDevice = audioDevice; - @this.fontManager = fontManager; - @this.helper = helper; - - @manager = spades::ui::UIManager(renderer, audioDevice); - @manager.RootElement.Font = fontManager.GuiFont; - - @clientMenu = ClientMenu(this); - clientMenu.Bounds = manager.RootElement.Bounds; - - @chatLogWindow = ChatLogWindow(this); - } - - void MouseEvent(float x, float y) { - manager.MouseEvent(x, y); - } - - void WheelEvent(float x, float y) { - manager.WheelEvent(x, y); - } - - void KeyEvent(string key, bool down) { - manager.KeyEvent(key, down); - } - - void TextInputEvent(string text) { - manager.TextInputEvent(text); - } - - void TextEditingEvent(string text, int start, int len) { - manager.TextEditingEvent(text, start, len); - } - - bool AcceptsTextInput() { - return manager.AcceptsTextInput; - } - - AABB2 GetTextInputRect() { - return manager.TextInputRect; - } - - void RunFrame(float dt) { - if(time < 0.f) { - time = 0.f; - } - - manager.RunFrame(dt); - if(activeUI !is null){ - manager.Render(); - } - - time += Min(dt, 0.05f); - } - - void Closing() { - - } - - bool WantsClientToBeClosed() { - return shouldExit; - } - - bool NeedsInput() { - return activeUI !is null; - } - - void set_ActiveUI(spades::ui::UIElement@ value) { - if(activeUI !is null) { - manager.RootElement.RemoveChild(activeUI); - } - @activeUI = value; - if(activeUI !is null) { - activeUI.Bounds = manager.RootElement.Bounds; - manager.RootElement.AddChild(activeUI); - } - manager.KeyPanic(); - } - spades::ui::UIElement@ get_ActiveUI(){ - return activeUI; - } - - void EnterClientMenu() { - @ActiveUI = clientMenu; - } - - void EnterTeamChatWindow() { - ClientChatWindow wnd(this, true); - @ActiveUI = wnd; - @manager.ActiveElement = wnd.field; - } - void EnterGlobalChatWindow() { - ClientChatWindow wnd(this, false); - @ActiveUI = wnd; - @manager.ActiveElement = wnd.field; - } - void EnterCommandWindow() { - ClientChatWindow wnd(this, true); - wnd.field.Text = "/"; - wnd.field.Select(1, 0); - wnd.UpdateState(); - @ActiveUI = wnd; - @manager.ActiveElement = wnd.field; - } - void CloseUI() { - @ActiveUI = null; - } - - void RecordChatLog(string text, Vector4 color) { - chatLogWindow.Record(text, color); - } - } - - class ClientMenu: spades::ui::UIElement { - private ClientUI@ ui; - private ClientUIHelper@ helper; - - ClientMenu(ClientUI@ ui) { - super(ui.manager); - @this.ui = ui; - @this.helper = ui.helper; - - float winW = 180.f, winH = 32.f * 4.f - 2.f; - float winX = (Manager.Renderer.ScreenWidth - winW) * 0.5f; - float winY = (Manager.Renderer.ScreenHeight - winH) * 0.5f; - - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.5f); - label.Bounds = AABB2(0.f, 0.f, - Manager.Renderer.ScreenWidth, Manager.Renderer.ScreenHeight); - AddChild(label); - } - - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.5f); - label.Bounds = AABB2(winX - 8.f, winY - 8.f, winW + 16.f, winH + 16.f); - AddChild(label); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Back to Game"); - button.Bounds = AABB2(winX, winY, winW, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnBackToGame); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Chat Log"); - button.Bounds = AABB2(winX, winY + 32.f, winW, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnChatLog); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Setup"); - button.Bounds = AABB2(winX, winY + 64.f, winW, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnSetup); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Disconnect"); - button.Bounds = AABB2(winX, winY + 96.f, winW, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnDisconnect); - AddChild(button); - } - } - - private void OnBackToGame(spades::ui::UIElement@ sender) { - @ui.ActiveUI = null; - } - private void OnSetup(spades::ui::UIElement@ sender) { - PreferenceViewOptions opt; - opt.GameActive = true; - - PreferenceView al(this, opt, ui.fontManager); - al.Run(); - } - private void OnChatLog(spades::ui::UIElement@ sender) { - @ui.ActiveUI = @ui.chatLogWindow; - ui.chatLogWindow.ScrollToEnd(); - } - private void OnDisconnect(spades::ui::UIElement@ sender) { - ui.shouldExit = true; - } - - void HotKey(string key) { - if(IsEnabled and key == "Escape") { - @ui.ActiveUI = null; - } else { - UIElement::HotKey(key); - } - } - } - - // Field with bash-like history support - class FieldWithHistory: spades::ui::Field { - array@ cmdhistory; - CommandHistoryItem@ temporalLastHistory; - uint currentHistoryIndex; - - FieldWithHistory(spades::ui::UIManager@ manager, array@ history) { - super(manager); - - @this.cmdhistory = history; - currentHistoryIndex = history.length; - @temporalLastHistory = this.CommandHistoryItemRep; - } - - private CommandHistoryItem@ CommandHistoryItemRep { - get { - return CommandHistoryItem(this.Text, this.SelectionStart, this.SelectionEnd); - } - set { - this.Text = value.text; - this.Select(value.selStart, value.selEnd - value.selStart); - } - } - - private void OverwriteItem() { - if(currentHistoryIndex < cmdhistory.length) { - @cmdhistory[currentHistoryIndex] = this.CommandHistoryItemRep; - }else if(currentHistoryIndex == cmdhistory.length) { - @temporalLastHistory = this.CommandHistoryItemRep; - } - } - - private void LoadItem() { - if(currentHistoryIndex < cmdhistory.length) { - @this.CommandHistoryItemRep = cmdhistory[currentHistoryIndex]; - }else if(currentHistoryIndex == cmdhistory.length) { - @this.CommandHistoryItemRep = temporalLastHistory; - } - } - - void KeyDown(string key) { - if(key == "Up") { - if(currentHistoryIndex > 0) { - OverwriteItem(); - currentHistoryIndex--; - LoadItem(); - } - }else if(key == "Down") { - if(currentHistoryIndex < cmdhistory.length) { - OverwriteItem(); - currentHistoryIndex++; - LoadItem(); - } - }else{ - Field::KeyDown(key); - } - } - - void CommandSent() { - cmdhistory.insertLast(this.CommandHistoryItemRep); - currentHistoryIndex = cmdhistory.length - 1; - } - - void Cancelled() { - OverwriteItem(); - } - }; - - uint StringCommonPrefixLength(string a, string b) { - for(uint i = 0, ln = Min(a.length, b.length); i < ln; i++) { - if(ToLower(a[i]) != ToLower(b[i])) return i; - } - return Min(a.length, b.length); - } - - /** Shows cvar's current value when user types something like "/cg_foobar" */ - class CommandFieldConfigValueView: spades::ui::UIElement { - string[]@ configNames; - string[] configValues; - CommandFieldConfigValueView(spades::ui::UIManager@ manager, string[] configNames) { - super(manager); - for(uint i = 0, len = configNames.length; i < len; i++) { - configValues.insertLast(ConfigItem(configNames[i]).StringValue); - } - @this.configNames = configNames; - } - void Render() { - float maxNameLen = 0.f; - float maxValueLen = 20.f; - Font@ font = this.Font; - Renderer@ renderer = this.Manager.Renderer; - float rowHeight = 25.f; - - for(uint i = 0, len = configNames.length; i < len; i++) { - maxNameLen = Max(maxNameLen, font.Measure(configNames[i]).x); - maxValueLen = Max(maxValueLen, font.Measure(configValues[i]).x); - } - Vector2 pos = this.ScreenPosition; - pos.y -= float(configNames.length) * rowHeight + 10.f; - - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.5f); - renderer.DrawImage(null, - AABB2(pos.x, pos.y, maxNameLen + maxValueLen + 20.f, - float(configNames.length) * rowHeight + 10.f)); - - for(uint i = 0, len = configNames.length; i < len; i++) { - font.DrawShadow(configNames[i], - pos + Vector2(5.f, 8.f + float(i) * rowHeight), - 1.f, Vector4(1,1,1,0.7), Vector4(0,0,0,0.3f)); - font.DrawShadow(configValues[i], - pos + Vector2(15.f + maxNameLen, 8.f + float(i) * rowHeight), - 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.4f)); - } - - } - } - - class CommandField: FieldWithHistory { - CommandFieldConfigValueView@ valueView; - - CommandField(spades::ui::UIManager@ manager, array@ history) { - super(manager, history); - - } - - void OnChanged() { - FieldWithHistory::OnChanged(); - - if(valueView !is null) { - @valueView.Parent = null; - } - if(Text.substr(0, 1) == "/" && - Text.substr(1, 1) != " ") { - int whitespace = Text.findFirst(" "); - if(whitespace < 0) { - whitespace = int(Text.length); - } - - string input = Text.substr(1, whitespace - 1); - if(input.length >= 2) { - string[]@ names = GetAllConfigNames(); - string[] filteredNames; - for(uint i = 0, len = names.length; i < len; i++) { - if ( - StringCommonPrefixLength(input, names[i]) == input.length && - !ConfigItem(names[i]).IsUnknown - ) { - filteredNames.insertLast(names[i]); - if(filteredNames.length >= 8) { - // too many - break; - } - } - } - if(filteredNames.length > 0) { - @valueView = CommandFieldConfigValueView(this.Manager, filteredNames); - valueView.Bounds = AABB2(0.f, -15.f, 0.f, 0.f); - @valueView.Parent = this; - } - } - } - } - - void KeyDown(string key) { - if(key == "Tab") { - if(SelectionLength == 0 && - SelectionStart == int(Text.length) && - Text.substr(0, 1) == "/" && - Text.findFirst(" ") < 0) { - // config variable auto completion - string input = Text.substr(1); - string[]@ names = GetAllConfigNames(); - string commonPart; - bool foundOne = false; - for(uint i = 0, len = names.length; i < len; i++) { - if ( - StringCommonPrefixLength(input, names[i]) == input.length && - !ConfigItem(names[i]).IsUnknown - ) { - if(!foundOne) { - commonPart = names[i]; - foundOne = true; - } - - uint commonLen = StringCommonPrefixLength(commonPart, names[i]); - commonPart = commonPart.substr(0, commonLen); - } - } - - if(commonPart.length > input.length) { - Text = "/" + commonPart; - Select(Text.length, 0); - } - - } - }else{ - FieldWithHistory::KeyDown(key); - } - } - } - - class ClientChatWindow: spades::ui::UIElement { - private ClientUI@ ui; - private ClientUIHelper@ helper; - - CommandField@ field; - spades::ui::Button@ sayButton; - spades::ui::SimpleButton@ teamButton; - spades::ui::SimpleButton@ globalButton; - - bool isTeamChat; - - ClientChatWindow(ClientUI@ ui, bool isTeamChat) { - super(ui.manager); - @this.ui = ui; - @this.helper = ui.helper; - this.isTeamChat = isTeamChat; - - float winW = Manager.Renderer.ScreenWidth * 0.7f, winH = 66.f; - float winX = (Manager.Renderer.ScreenWidth - winW) * 0.5f; - float winY = (Manager.Renderer.ScreenHeight - winH) - 20.f; - /* - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.5f); - label.Bounds = Bounds; - AddChild(label); - }*/ - - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.5f); - label.Bounds = AABB2(winX - 8.f, winY - 8.f, winW + 16.f, winH + 16.f); - AddChild(label); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Say"); - button.Bounds = AABB2(winX + winW - 244.f, winY + 36.f, 120.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnSay); - AddChild(button); - @sayButton = button; - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Cancel"); - button.Bounds = AABB2(winX + winW - 120.f, winY + 36.f, 120.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnCancel); - AddChild(button); - } - { - @field = CommandField(Manager, ui.chatHistory); - field.Bounds = AABB2(winX, winY, winW, 30.f); - field.Placeholder = _Tr("Client", "Chat Text"); - @field.Changed = spades::ui::EventHandler(this.OnFieldChanged); - AddChild(field); - } - { - @globalButton = spades::ui::SimpleButton(Manager); - globalButton.Toggle = true; - globalButton.Toggled = isTeamChat == false; - globalButton.Caption = _Tr("Client", "Global"); - globalButton.Bounds = AABB2(winX, winY + 36.f, 70.f, 30.f); - @globalButton.Activated = spades::ui::EventHandler(this.OnSetGlobal); - AddChild(globalButton); - } - { - @teamButton = spades::ui::SimpleButton(Manager); - teamButton.Toggle = true; - teamButton.Toggled = isTeamChat == true; - teamButton.Caption = _Tr("Client", "Team"); - teamButton.Bounds = AABB2(winX + 70.f, winY + 36.f, 70.f, 30.f); - @teamButton.Activated = spades::ui::EventHandler(this.OnSetTeam); - AddChild(teamButton); - } - } - - void UpdateState() { - sayButton.Enable = field.Text.length > 0; - } - - bool IsTeamChat { - get final { return isTeamChat; } - set { - if(isTeamChat == value) return; - isTeamChat = value; - teamButton.Toggled = isTeamChat; - globalButton.Toggled = not isTeamChat; - UpdateState(); - } - } - - private void OnSetGlobal(spades::ui::UIElement@ sender) { - IsTeamChat = false; - } - private void OnSetTeam(spades::ui::UIElement@ sender) { - IsTeamChat = true; - } - - private void OnFieldChanged(spades::ui::UIElement@ sender) { - UpdateState(); - } - - private void Close() { - @ui.ActiveUI = null; - } - - private void OnCancel(spades::ui::UIElement@ sender) { - field.Cancelled(); - Close(); - } - - private bool CheckAndSetConfigVariable() { - string text = field.Text; - if(text.substr(0, 1) != "/") return false; - int idx = text.findFirst(" "); - if(idx < 2) return false; - - // find variable - string varname = text.substr(1, idx - 1); - string[] vars = GetAllConfigNames(); - - for(uint i = 0, len = vars.length; i < len; i++) { - if(vars[i].length == varname.length && - StringCommonPrefixLength(vars[i], varname) == vars[i].length) { - // match - string val = text.substr(idx + 1); - ConfigItem item(vars[i]); - item.StringValue = val; - return true; - } - } - - return false; - } - - private void OnSay(spades::ui::UIElement@ sender) { - field.CommandSent(); - if(!CheckAndSetConfigVariable()) { - if(isTeamChat) - ui.helper.SayTeam(field.Text); - else - ui.helper.SayGlobal(field.Text); - } - Close(); - } - - void HotKey(string key) { - if(IsEnabled and key == "Escape") { - OnCancel(this); - }else if(IsEnabled and key == "Enter") { - if(field.Text.length == 0) { - OnCancel(this); - }else{ - OnSay(this); - } - } else { - UIElement::HotKey(key); - } - } - } - - class ChatLogSayWindow: ClientChatWindow { - ChatLogWindow@ owner; - - ChatLogSayWindow(ChatLogWindow@ own, bool isTeamChat) { - super(own.ui, isTeamChat); - @owner = own; - } - - void Close() { - owner.SayWindowClosed(); - @this.Parent = null; - } - } - - class ChatLogWindow: spades::ui::UIElement { - - float contentsTop, contentsHeight; - - ClientUI@ ui; - private ClientUIHelper@ helper; - - private spades::ui::TextViewer@ viewer; - ChatLogSayWindow@ sayWindow; - - private spades::ui::UIElement@ sayButton1; - private spades::ui::UIElement@ sayButton2; - - ChatLogWindow(ClientUI@ ui) { - super(ui.manager); - @this.ui = ui; - @this.helper = ui.helper; - - @Font = Manager.RootElement.Font; - this.Bounds = Manager.RootElement.Bounds; - - float contentsWidth = 700.f; - float contentsLeft = (Manager.Renderer.ScreenWidth - contentsWidth) * 0.5f; - contentsHeight = Manager.Renderer.ScreenHeight - 200.f; - contentsTop = (Manager.Renderer.ScreenHeight - contentsHeight - 106.f) * 0.5f; - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.4f); - label.Bounds = Bounds; - AddChild(label); - } - { - spades::ui::Label label(Manager); - label.BackgroundColor = Vector4(0, 0, 0, 0.8f); - label.Bounds = AABB2(0.f, contentsTop - 13.f, Size.x, contentsHeight + 27.f); - AddChild(label); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Close"); - button.Bounds = AABB2( - contentsLeft + contentsWidth - 150.f, - contentsTop + contentsHeight - 30.f - , 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnOkPressed); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Say Global"); - button.Bounds = AABB2( - contentsLeft, - contentsTop + contentsHeight - 30.f - , 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnGlobalChat); - AddChild(button); - @this.sayButton1 = button; - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("Client", "Say Team"); - button.Bounds = AABB2( - contentsLeft + 155.f, - contentsTop + contentsHeight - 30.f - , 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnTeamChat); - AddChild(button); - @this.sayButton2 = button; - } - { - spades::ui::TextViewer viewer(Manager); - AddChild(viewer); - viewer.Bounds = AABB2(contentsLeft, contentsTop, contentsWidth, contentsHeight - 40.f); - @this.viewer = viewer; - } - } - - void ScrollToEnd() { - viewer.Layout(); - viewer.ScrollToEnd(); - } - - void Close() { - @ui.ActiveUI = null; - } - - void SayWindowClosed() { - @sayWindow = null; - sayButton1.Enable = true; - sayButton2.Enable = true; - } - - private void OnOkPressed(spades::ui::UIElement@ sender) { - Close(); - } - - private void OnTeamChat(spades::ui::UIElement@ sender) { - if(sayWindow !is null) { - sayWindow.IsTeamChat = true; - return; - } - sayButton1.Enable = false; - sayButton2.Enable = false; - ChatLogSayWindow wnd(this, true); - AddChild(wnd); - wnd.Bounds = this.Bounds; - @this.sayWindow = wnd; - @Manager.ActiveElement = wnd.field; - } - - private void OnGlobalChat(spades::ui::UIElement@ sender) { - if(sayWindow !is null) { - sayWindow.IsTeamChat = false; - return; - } - sayButton1.Enable = false; - sayButton2.Enable = false; - ChatLogSayWindow wnd(this, false); - AddChild(wnd); - wnd.Bounds = this.Bounds; - @this.sayWindow = wnd; - @Manager.ActiveElement = wnd.field; - } - - void HotKey(string key) { - if(sayWindow !is null) { - UIElement::HotKey(key); - return; - } - if(IsEnabled and (key == "Escape")) { - Close(); - } else if(IsEnabled and (key == "y")) { - OnTeamChat(this); - } else if(IsEnabled and (key == "t")) { - OnGlobalChat(this); - } else { - UIElement::HotKey(key); - } - } - - void Record(string text, Vector4 color) { - viewer.AddLine(text, this.IsVisible, color); - } - - void Render() { - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Renderer@ r = Manager.Renderer; - Image@ img = r.RegisterImage("Gfx/White.tga"); - - r.ColorNP = Vector4(1, 1, 1, 0.08f); - r.DrawImage(img, - AABB2(pos.x, pos.y + contentsTop - 15.f, size.x, 1.f)); - r.DrawImage(img, - AABB2(pos.x, pos.y + contentsTop + contentsHeight + 15.f, size.x, 1.f)); - r.ColorNP = Vector4(1, 1, 1, 0.2f); - r.DrawImage(img, - AABB2(pos.x, pos.y + contentsTop - 14.f, size.x, 1.f)); - r.DrawImage(img, - AABB2(pos.x, pos.y + contentsTop + contentsHeight + 14.f, size.x, 1.f)); - - UIElement::Render(); - } - - } - - ClientUI@ CreateClientUI(Renderer@ renderer, AudioDevice@ audioDevice, - FontManager@ fontManager, ClientUIHelper@ helper) { - return ClientUI(renderer, audioDevice, fontManager, helper); - } -} diff --git a/Resources/Scripts/Gui/Gui.as b/Resources/Scripts/Gui/Gui.as index d9ccc079..0fd6dac8 100644 --- a/Resources/Scripts/Gui/Gui.as +++ b/Resources/Scripts/Gui/Gui.as @@ -1,27 +1,26 @@ /* Copyright (c) 2013 yvt - + This file is part of OpenSpades. - + OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . - + */ -#include "UIFramework.as" -#include "UIControls.as" +#include "UIFramework/UIFramework.as" #include "MessageBox.as" -#include "MainScreen.as" -#include "StartupScreen.as" +#include "MainScreen/MainScreenUI.as" +#include "StartupScreen/StartupScreenUI.as" #include "Preferences.as" -#include "ClientUI.as" +#include "Client/ClientUI.as" diff --git a/Resources/Scripts/Gui/MainScreen.as b/Resources/Scripts/Gui/MainScreen.as deleted file mode 100644 index 184deed8..00000000 --- a/Resources/Scripts/Gui/MainScreen.as +++ /dev/null @@ -1,838 +0,0 @@ -/* - Copyright (c) 2013 yvt - - This file is part of OpenSpades. - - OpenSpades is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenSpades is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OpenSpades. If not, see . - - */ - -#include "Flags.as" -#include "CreateProfileScreen.as" - -namespace spades { - - - class MainScreenUI { - private Renderer@ renderer; - private AudioDevice@ audioDevice; - FontManager@ fontManager; - MainScreenHelper@ helper; - - spades::ui::UIManager@ manager; - - MainScreenMainMenu@ mainMenu; - - bool shouldExit = false; - - private float time = -1.f; - - private ConfigItem cg_playerName("cg_playerName"); - private ConfigItem cg_playerNameIsSet("cg_playerNameIsSet", "0"); - - MainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, MainScreenHelper@ helper) { - @this.renderer = renderer; - @this.audioDevice = audioDevice; - @this.fontManager = fontManager; - @this.helper = helper; - - SetupRenderer(); - - @manager = spades::ui::UIManager(renderer, audioDevice); - @manager.RootElement.Font = fontManager.GuiFont; - - @mainMenu = MainScreenMainMenu(this); - mainMenu.Bounds = manager.RootElement.Bounds; - manager.RootElement.AddChild(mainMenu); - - // Let the new player choose their IGN - if (cg_playerName.StringValue != "" && - cg_playerName.StringValue != "Deuce") { - cg_playerNameIsSet.IntValue = 1; - } - if (cg_playerNameIsSet.IntValue == 0) { - CreateProfileScreen al(mainMenu); - al.Run(); - } - - } - - void SetupRenderer() { - // load map - @renderer.GameMap = GameMap("Maps/Title.vxl"); - renderer.FogColor = Vector3(0.1f, 0.10f, 0.1f); - renderer.FogDistance = 128.f; - time = -1.f; - - // returned from the client game, so reload the server list. - if(mainMenu !is null) - mainMenu.LoadServerList(); - - if(manager !is null) - manager.KeyPanic(); - } - - void MouseEvent(float x, float y) { - manager.MouseEvent(x, y); - } - - void WheelEvent(float x, float y) { - manager.WheelEvent(x, y); - } - - void KeyEvent(string key, bool down) { - manager.KeyEvent(key, down); - } - - void TextInputEvent(string text) { - manager.TextInputEvent(text); - } - - void TextEditingEvent(string text, int start, int len) { - manager.TextEditingEvent(text, start, len); - } - - bool AcceptsTextInput() { - return manager.AcceptsTextInput; - } - - AABB2 GetTextInputRect() { - return manager.TextInputRect; - } - - private SceneDefinition SetupCamera(SceneDefinition sceneDef, - Vector3 eye, Vector3 at, Vector3 up, float fov) { - Vector3 dir = (at - eye).Normalized; - Vector3 side = Cross(dir, up).Normalized; - up = -Cross(dir, side); - sceneDef.viewOrigin = eye; - sceneDef.viewAxisX = side; - sceneDef.viewAxisY = up; - sceneDef.viewAxisZ = dir; - sceneDef.fovY = fov * 3.141592654f / 180.f; - sceneDef.fovX = atan(tan(sceneDef.fovY * 0.5f) * renderer.ScreenWidth / renderer.ScreenHeight) * 2.f; - return sceneDef; - } - - void RunFrame(float dt) { - if(time < 0.f) { - time = 0.f; - } - - SceneDefinition sceneDef; - float cameraX = time; - cameraX -= floor(cameraX / 512.f) * 512.f; - cameraX = 512.f - cameraX; - sceneDef = SetupCamera(sceneDef, - Vector3(cameraX, 256.f, 12.f), Vector3(cameraX + .1f, 257.f, 12.5f), Vector3(0.f, 0.f, -1.f), - 30.f); - sceneDef.zNear = 0.1f; - sceneDef.zFar = 222.f; - sceneDef.time = int(time * 1000.f); - sceneDef.viewportWidth = int(renderer.ScreenWidth); - sceneDef.viewportHeight = int(renderer.ScreenHeight); - sceneDef.denyCameraBlur = true; - sceneDef.depthOfFieldFocalLength = 100.f; - sceneDef.skipWorld = false; - - // fade the map - float fade = Clamp((time - 1.f) / 2.2f, 0.f, 1.f); - sceneDef.globalBlur = Clamp((1.f - (time - 1.f) / 2.5f), 0.f, 1.f); - if(!mainMenu.IsEnabled) { - sceneDef.globalBlur = Max(sceneDef.globalBlur, 0.5f); - } - - renderer.StartScene(sceneDef); - renderer.EndScene(); - - // fade the map (draw) - if(fade < 1.f) { - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 1.f - fade); - renderer.DrawImage(renderer.RegisterImage("Gfx/White.tga"), - AABB2(0.f, 0.f, renderer.ScreenWidth, renderer.ScreenHeight)); - } - - // draw title logo - Image@ img = renderer.RegisterImage("Gfx/Title/Logo.png"); - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 1.f); - renderer.DrawImage(img, Vector2((renderer.ScreenWidth - img.Width) * 0.5f, 64.f)); - - manager.RunFrame(dt); - manager.Render(); - - renderer.FrameDone(); - renderer.Flip(); - time += Min(dt, 0.05f); - } - - void Closing() { - shouldExit = true; - } - - bool WantsToBeClosed() { - return shouldExit; - } - } - - class ServerListItem: spades::ui::ButtonBase { - MainScreenServerItem@ item; - FlagIconRenderer@ flagIconRenderer; - ServerListItem(spades::ui::UIManager@ manager, MainScreenServerItem@ item){ - super(manager); - @this.item = item; - @flagIconRenderer = FlagIconRenderer(manager.Renderer); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - - Vector4 bgcolor = Vector4(1.f, 1.f, 1.f, 0.0f); - Vector4 fgcolor = Vector4(1.f, 1.f, 1.f, 1.f); - if(item.Favorite) { - bgcolor = Vector4(0.3f, 0.3f, 1.f, 0.1f); - fgcolor = Vector4(220.f/255.f,220.f/255.f,0,1); - } - if(Pressed && Hover) { - bgcolor.w += 0.3; - } else if(Hover) { - bgcolor.w += 0.15; - } - renderer.ColorNP = bgcolor; - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - - Font.Draw(item.Name, ScreenPosition + Vector2(4.f, 2.f), 1.f, fgcolor); - string playersStr = ToString(item.NumPlayers) + "/" + ToString(item.MaxPlayers); - Vector4 col(1,1,1,1); - if(item.NumPlayers >= item.MaxPlayers) col = Vector4(1,0.7f,0.7f,1); - else if(item.NumPlayers >= item.MaxPlayers * 3 / 4) col = Vector4(1,1,0.7f,1); - else if(item.NumPlayers == 0) col = Vector4(0.7f,0.7f,1,1); - Font.Draw(playersStr, ScreenPosition + Vector2(340.f-Font.Measure(playersStr).x * 0.5f, 2.f), 1.f, col); - Font.Draw(item.MapName, ScreenPosition + Vector2(400.f, 2.f), 1.f, Vector4(1,1,1,1)); - Font.Draw(item.GameMode, ScreenPosition + Vector2(550.f, 2.f), 1.f, Vector4(1,1,1,1)); - Font.Draw(item.Protocol, ScreenPosition + Vector2(630.f, 2.f), 1.f, Vector4(1,1,1,1)); - if(not flagIconRenderer.DrawIcon(item.Country, ScreenPosition + Vector2(700.f, size.y * 0.5f))) { - Font.Draw(item.Country, ScreenPosition + Vector2(680.f, 2.f), 1.f, Vector4(1,1,1,1)); - } - } - } - - funcdef void ServerListItemEventHandler(ServerListModel@ sender, MainScreenServerItem@ item); - - class ServerListModel: spades::ui::ListViewModel { - spades::ui::UIManager@ manager; - MainScreenServerItem@[]@ list; - - ServerListItemEventHandler@ ItemActivated; - ServerListItemEventHandler@ ItemDoubleClicked; - ServerListItemEventHandler@ ItemRightClicked; - - ServerListModel(spades::ui::UIManager@ manager, MainScreenServerItem@[]@ list) { - @this.manager = manager; - @this.list = list; - } - int NumRows { - get { return int(list.length); } - } - private void OnItemClicked(spades::ui::UIElement@ sender){ - ServerListItem@ item = cast(sender); - if(ItemActivated !is null) { - ItemActivated(this, item.item); - } - } - private void OnItemDoubleClicked(spades::ui::UIElement@ sender){ - ServerListItem@ item = cast(sender); - if(ItemDoubleClicked !is null) { - ItemDoubleClicked(this, item.item); - } - } - private void OnItemRightClicked(spades::ui::UIElement@ sender){ - ServerListItem@ item = cast(sender); - if(ItemRightClicked !is null) { - ItemRightClicked(this, item.item); - } - } - spades::ui::UIElement@ CreateElement(int row) { - ServerListItem i(manager, list[row]); - @i.Activated = spades::ui::EventHandler(this.OnItemClicked); - @i.DoubleClicked = spades::ui::EventHandler(this.OnItemDoubleClicked); - @i.RightClicked = spades::ui::EventHandler(this.OnItemRightClicked); - return i; - } - void RecycleElement(spades::ui::UIElement@ elem) {} - } - - - class ServerListHeader: spades::ui::ButtonBase { - string Text; - ServerListHeader(spades::ui::UIManager@ manager){ - super(manager); - } - void OnActivated() { - ButtonBase::OnActivated(); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - if(Pressed && Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.15f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.0f); - } - renderer.DrawImage(img, AABB2(pos.x - 2.f, pos.y, size.x, size.y)); - - Font.Draw(Text, ScreenPosition + Vector2(0.f, 2.f), 1.f, Vector4(1,1,1,1)); - } - } - - - class RefreshButton: spades::ui::SimpleButton { - RefreshButton(spades::ui::UIManager@ manager){ - super(manager); - } - void Render() { - SimpleButton::Render(); - - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/UI/Refresh.png"); - renderer.DrawImage(img, pos + (size - Vector2(16.f, 16.f)) * 0.5f); - } - } - - class ProtocolButton: spades::ui::SimpleButton { - ProtocolButton(spades::ui::UIManager@ manager){ - super(manager); - Toggle = true; - } - } - - uint8 ToLower(uint8 c) { - if(c >= uint8(0x41) and c <= uint8(0x5a)) { - return uint8(c - 0x41 + 0x61); - } else { - return c; - } - } - bool StringContainsCaseInsensitive(string text, string pattern) { - for(int i = text.length - 1; i >= 0; i--) - text[i] = ToLower(text[i]); - for(int i = pattern.length - 1; i >= 0; i--) - pattern[i] = ToLower(pattern[i]); - return text.findFirst(pattern) >= 0; - } - - class MainScreenMainMenu: spades::ui::UIElement { - - MainScreenUI@ ui; - MainScreenHelper@ helper; - spades::ui::Field@ addressField; - - spades::ui::Button@ protocol3Button; - spades::ui::Button@ protocol4Button; - - spades::ui::Button@ filterProtocol3Button; - spades::ui::Button@ filterProtocol4Button; - spades::ui::Button@ filterEmptyButton; - spades::ui::Button@ filterFullButton; - spades::ui::Field@ filterField; - - spades::ui::ListView@ serverList; - MainScreenServerListLoadingView@ loadingView; - MainScreenServerListErrorView@ errorView; - bool loading = false, loaded = false; - - private ConfigItem cg_protocolVersion("cg_protocolVersion", "3"); - private ConfigItem cg_lastQuickConnectHost("cg_lastQuickConnectHost", "127.0.0.1"); - private ConfigItem cg_serverlistSort("cg_serverlistSort", "16385"); - - MainScreenMainMenu(MainScreenUI@ ui) { - super(ui.manager); - @this.ui = ui; - @this.helper = ui.helper; - - float contentsWidth = 750.f; - float contentsLeft = (Manager.Renderer.ScreenWidth - contentsWidth) * 0.5f; - float footerPos = Manager.Renderer.ScreenHeight - 50.f; - { - spades::ui::Button button(Manager); - button.Caption = _Tr("MainScreen", "Connect"); - button.Bounds = AABB2(contentsLeft + contentsWidth - 150.f, 200.f, 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnConnectPressed); - AddChild(button); - } - { - @addressField = spades::ui::Field(Manager); - addressField.Bounds = AABB2(contentsLeft, 200, contentsWidth - 240.f, 30.f); - addressField.Placeholder = _Tr("MainScreen", "Quick Connect"); - addressField.Text = cg_lastQuickConnectHost.StringValue; - @addressField.Changed = spades::ui::EventHandler(this.OnAddressChanged); - AddChild(addressField); - } - { - @protocol3Button = ProtocolButton(Manager); - protocol3Button.Bounds = AABB2(contentsLeft + contentsWidth - 240.f + 6.f, 200, - 40.f, 30.f); - protocol3Button.Caption = _Tr("MainScreen", "0.75"); - @protocol3Button.Activated = spades::ui::EventHandler(this.OnProtocol3Pressed); - protocol3Button.Toggle = true; - protocol3Button.Toggled = cg_protocolVersion.IntValue == 3; - AddChild(protocol3Button); - } - { - @protocol4Button = ProtocolButton(Manager); - protocol4Button.Bounds = AABB2(contentsLeft + contentsWidth - 200.f + 6.f, 200, - 40.f, 30.f); - protocol4Button.Caption = _Tr("MainScreen", "0.76"); - @protocol4Button.Activated = spades::ui::EventHandler(this.OnProtocol4Pressed); - protocol4Button.Toggle = true; - protocol4Button.Toggled = cg_protocolVersion.IntValue == 4; - AddChild(protocol4Button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("MainScreen", "Quit"); - button.Bounds = AABB2(contentsLeft + contentsWidth - 100.f, footerPos, 100.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnQuitPressed); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("MainScreen", "Credits"); - button.Bounds = AABB2(contentsLeft + contentsWidth - 202.f, footerPos, 100.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnCreditsPressed); - AddChild(button); - } - { - spades::ui::Button button(Manager); - button.Caption = _Tr("MainScreen", "Setup"); - button.Bounds = AABB2(contentsLeft + contentsWidth - 304.f, footerPos, 100.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnSetupPressed); - AddChild(button); - } - { - RefreshButton button(Manager); - button.Bounds = AABB2(contentsLeft + contentsWidth - 364.f, footerPos, 30.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnRefreshServerListPressed); - AddChild(button); - } - { - spades::ui::Label label(Manager); - label.Text = _Tr("MainScreen", "Filter"); - label.Bounds = AABB2(contentsLeft, footerPos, 50.f, 30.f); - label.Alignment = Vector2(0.f, 0.5f); - AddChild(label); - } - { - @filterProtocol3Button = ProtocolButton(Manager); - filterProtocol3Button.Bounds = AABB2(contentsLeft + 50.f, footerPos, - 40.f, 30.f); - filterProtocol3Button.Caption = _Tr("MainScreen", "0.75"); - @filterProtocol3Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol3Pressed); - filterProtocol3Button.Toggle = true; - AddChild(filterProtocol3Button); - } - { - @filterProtocol4Button = ProtocolButton(Manager); - filterProtocol4Button.Bounds = AABB2(contentsLeft + 90.f, footerPos, - 40.f, 30.f); - filterProtocol4Button.Caption = _Tr("MainScreen", "0.76"); - @filterProtocol4Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol4Pressed); - filterProtocol4Button.Toggle = true; - AddChild(filterProtocol4Button); - } - { - @filterEmptyButton = ProtocolButton(Manager); - filterEmptyButton.Bounds = AABB2(contentsLeft + 135.f, footerPos, - 50.f, 30.f); - filterEmptyButton.Caption = _Tr("MainScreen", "Empty"); - @filterEmptyButton.Activated = spades::ui::EventHandler(this.OnFilterEmptyPressed); - filterEmptyButton.Toggle = true; - AddChild(filterEmptyButton); - } - { - @filterFullButton = ProtocolButton(Manager); - filterFullButton.Bounds = AABB2(contentsLeft + 185.f, footerPos, - 70.f, 30.f); - filterFullButton.Caption = _Tr("MainScreen", "Not Full"); - @filterFullButton.Activated = spades::ui::EventHandler(this.OnFilterFullPressed); - filterFullButton.Toggle = true; - AddChild(filterFullButton); - } - { - @filterField = spades::ui::Field(Manager); - filterField.Bounds = AABB2(contentsLeft + 260.f, footerPos, 120.f, 30.f); - filterField.Placeholder = _Tr("MainScreen", "Filter"); - @filterField.Changed = spades::ui::EventHandler(this.OnFilterTextChanged); - AddChild(filterField); - } - { - @serverList = spades::ui::ListView(Manager); - serverList.Bounds = AABB2(contentsLeft, 270.f, contentsWidth, footerPos - 280.f); - AddChild(serverList); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 2.f, 240.f, 300.f - 2.f, 30.f); - header.Text = _Tr("MainScreen", "Server Name"); - @header.Activated = spades::ui::EventHandler(this.SortServerListByName); - AddChild(header); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 300.f, 240.f, 100.f, 30.f); - header.Text = _Tr("MainScreen", "Players"); - @header.Activated = spades::ui::EventHandler(this.SortServerListByNumPlayers); - AddChild(header); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 400.f, 240.f, 150.f, 30.f); - header.Text = _Tr("MainScreen", "Map Name"); - @header.Activated = spades::ui::EventHandler(this.SortServerListByMapName); - AddChild(header); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 550.f, 240.f, 80.f, 30.f); - header.Text = _Tr("MainScreen", "Game Mode"); - @header.Activated = spades::ui::EventHandler(this.SortServerListByGameMode); - AddChild(header); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 630.f, 240.f, 50.f, 30.f); - header.Text = _Tr("MainScreen", "Ver."); - @header.Activated = spades::ui::EventHandler(this.SortServerListByProtocol); - AddChild(header); - } - { - ServerListHeader header(Manager); - header.Bounds = AABB2(contentsLeft + 680.f, 240.f, 50.f, 30.f); - header.Text = _Tr("MainScreen", "Loc."); - @header.Activated = spades::ui::EventHandler(this.SortServerListByCountry); - AddChild(header); - } - { - @loadingView = MainScreenServerListLoadingView(Manager); - loadingView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f); - loadingView.Visible = false; - AddChild(loadingView); - } - { - @errorView = MainScreenServerListErrorView(Manager); - errorView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f); - errorView.Visible = false; - AddChild(errorView); - } - LoadServerList(); - } - - void LoadServerList() { - if(loading) { - return; - } - loaded = false; - loading = true; - @serverList.Model = spades::ui::ListViewModel(); // empty - errorView.Visible = false; - loadingView.Visible = true; - helper.StartQuery(); - } - - void ServerListItemActivated(ServerListModel@ sender, MainScreenServerItem@ item) { - addressField.Text = item.Address; - cg_lastQuickConnectHost = addressField.Text; - if(item.Protocol == "0.75") { - SetProtocolVersion(3); - }else if(item.Protocol == "0.76") { - SetProtocolVersion(4); - } - addressField.SelectAll(); - } - - void ServerListItemDoubleClicked(ServerListModel@ sender, MainScreenServerItem@ item) { - ServerListItemActivated(sender, item); - - // Double-click to connect - Connect(); - } - - void ServerListItemRightClicked(ServerListModel@ sender, MainScreenServerItem@ item) { - helper.SetServerFavorite(item.Address, !item.Favorite); - UpdateServerList(); - } - - private void SortServerListByPing(spades::ui::UIElement@ sender) { - SortServerList(0); - } - private void SortServerListByNumPlayers(spades::ui::UIElement@ sender) { - SortServerList(1); - } - private void SortServerListByName(spades::ui::UIElement@ sender) { - SortServerList(2); - } - private void SortServerListByMapName(spades::ui::UIElement@ sender) { - SortServerList(3); - } - private void SortServerListByGameMode(spades::ui::UIElement@ sender) { - SortServerList(4); - } - private void SortServerListByProtocol(spades::ui::UIElement@ sender) { - SortServerList(5); - } - private void SortServerListByCountry(spades::ui::UIElement@ sender) { - SortServerList(6); - } - - private void SortServerList(int keyId) { - int sort = cg_serverlistSort.IntValue; - if(int(sort & 0xfff) == keyId) { - sort ^= int(0x4000); - } else { - sort = keyId; - } - cg_serverlistSort = sort; - UpdateServerList(); - } - - private void UpdateServerList() { - string key = ""; - switch(cg_serverlistSort.IntValue & 0xfff) { - case 0: key = "Ping"; break; - case 1: key = "NumPlayers"; break; - case 2: key = "Name"; break; - case 3: key = "MapName"; break; - case 4: key = "GameMode"; break; - case 5: key = "Protocol"; break; - case 6: key = "Country"; break; - } - MainScreenServerItem@[]@ list = helper.GetServerList(key, - (cg_serverlistSort.IntValue & 0x4000) != 0); - if((list is null) or (loading)){ - @serverList.Model = spades::ui::ListViewModel(); // empty - return; - } - - // filter the server list - bool filterProtocol3 = filterProtocol3Button.Toggled; - bool filterProtocol4 = filterProtocol4Button.Toggled; - bool filterEmpty = filterEmptyButton.Toggled; - bool filterFull = filterFullButton.Toggled; - string filterText = filterField.Text; - MainScreenServerItem@[]@ list2 = array(); - for(int i = 0, count = list.length; i < count; i++) { - MainScreenServerItem@ item = list[i]; - if(filterProtocol3 and (item.Protocol != "0.75")) { - continue; - } - if(filterProtocol4 and (item.Protocol != "0.76")) { - continue; - } - if(filterEmpty and (item.NumPlayers > 0)) { - continue; - } - if(filterFull and (item.NumPlayers >= item.MaxPlayers)) { - continue; - } - if(filterText.length > 0) { - if(not (StringContainsCaseInsensitive(item.Name, filterText) or - StringContainsCaseInsensitive(item.MapName, filterText) or - StringContainsCaseInsensitive(item.GameMode, filterText))) { - continue; - } - } - list2.insertLast(item); - } - - ServerListModel model(Manager, list2); - @serverList.Model = model; - @model.ItemActivated = ServerListItemEventHandler(this.ServerListItemActivated); - @model.ItemDoubleClicked = ServerListItemEventHandler(this.ServerListItemDoubleClicked); - @model.ItemRightClicked = ServerListItemEventHandler(this.ServerListItemRightClicked); - serverList.ScrollToTop(); - } - - private void CheckServerList() { - if(helper.PollServerListState()) { - MainScreenServerItem@[]@ list = helper.GetServerList("", false); - if(list is null or list.length == 0) { - // failed. - // FIXME: show error message? - loaded = false; loading = false; - errorView.Visible = true; - loadingView.Visible = false; - @serverList.Model = spades::ui::ListViewModel(); // empty - return; - } - loading = false; - loaded = true; - errorView.Visible = false; - loadingView.Visible = false; - UpdateServerList(); - } - } - - private void OnAddressChanged(spades::ui::UIElement@ sender) { - cg_lastQuickConnectHost = addressField.Text; - } - - private void SetProtocolVersion(int ver) { - protocol3Button.Toggled = (ver == 3); - protocol4Button.Toggled = (ver == 4); - cg_protocolVersion = ver; - } - - private void OnProtocol3Pressed(spades::ui::UIElement@ sender) { - SetProtocolVersion(3); - } - - private void OnProtocol4Pressed(spades::ui::UIElement@ sender) { - SetProtocolVersion(4); - } - - private void OnFilterProtocol3Pressed(spades::ui::UIElement@ sender) { - filterProtocol4Button.Toggled = false; - UpdateServerList(); - } - private void OnFilterProtocol4Pressed(spades::ui::UIElement@ sender) { - filterProtocol3Button.Toggled = false; - UpdateServerList(); - } - private void OnFilterFullPressed(spades::ui::UIElement@ sender) { - filterEmptyButton.Toggled = false; - UpdateServerList(); - } - private void OnFilterEmptyPressed(spades::ui::UIElement@ sender) { - filterFullButton.Toggled = false; - UpdateServerList(); - } - private void OnFilterTextChanged(spades::ui::UIElement@ sender) { - UpdateServerList(); - } - - private void OnRefreshServerListPressed(spades::ui::UIElement@ sender) { - LoadServerList(); - } - - private void OnQuitPressed(spades::ui::UIElement@ sender) { - ui.shouldExit = true; - } - - private void OnCreditsPressed(spades::ui::UIElement@ sender) { - AlertScreen al(this, ui.helper.Credits, Min(500.f, Manager.Renderer.ScreenHeight - 100.f)); - al.Run(); - } - - private void OnSetupPressed(spades::ui::UIElement@ sender) { - PreferenceView al(this, PreferenceViewOptions(), ui.fontManager); - al.Run(); - } - - private void Connect() { - string msg = helper.ConnectServer(addressField.Text, cg_protocolVersion.IntValue); - if(msg.length > 0) { - // failde to initialize client. - AlertScreen al(this, msg); - al.Run(); - } - } - - private void OnConnectPressed(spades::ui::UIElement@ sender) { - Connect(); - } - - void HotKey(string key) { - if(IsEnabled and key == "Enter") { - Connect(); - } else if(IsEnabled and key == "Escape") { - ui.shouldExit = true; - } else { - UIElement::HotKey(key); - } - } - - void Render() { - CheckServerList(); - UIElement::Render(); - - // check for client error message. - if(IsEnabled) { - string msg = helper.GetPendingErrorMessage(); - if(msg.length > 0) { - // try to maek the "disconnected" message more friendly. - if(msg.findFirst("Disconnected:") >= 0) { - int ind1 = msg.findFirst("Disconnected:"); - int ind2 = msg.findFirst("\n", ind1); - if(ind2 < 0) ind2 = msg.length; - ind1 += "Disconnected:".length; - msg = msg.substr(ind1, ind2 - ind1); - msg = _Tr("MainScreen", "You were disconnected from the server because of the following reason:\n\n{0}", msg); - } - - // failed to connect. - AlertScreen al(this, msg); - al.Run(); - } - } - } - } - - class MainScreenServerListLoadingView: spades::ui::UIElement { - MainScreenServerListLoadingView(spades::ui::UIManager@ manager) { - super(manager); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Font@ font = this.Font; - string text = _Tr("MainScreen", "Loading..."); - Vector2 txtSize = font.Measure(text); - Vector2 txtPos; - txtPos = pos + (size - txtSize) * 0.5f; - - font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8)); - } - } - - class MainScreenServerListErrorView: spades::ui::UIElement { - MainScreenServerListErrorView(spades::ui::UIManager@ manager) { - super(manager); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Font@ font = this.Font; - string text = _Tr("MainScreen", "Failed to fetch the server list."); - Vector2 txtSize = font.Measure(text); - Vector2 txtPos; - txtPos = pos + (size - txtSize) * 0.5f; - - font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8)); - } - } - - MainScreenUI@ CreateMainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, - FontManager@ fontManager, MainScreenHelper@ helper) { - return MainScreenUI(renderer, audioDevice, fontManager, helper); - } -} diff --git a/Resources/Scripts/Gui/Flags.as b/Resources/Scripts/Gui/MainScreen/CountryFlags.as similarity index 100% rename from Resources/Scripts/Gui/Flags.as rename to Resources/Scripts/Gui/MainScreen/CountryFlags.as diff --git a/Resources/Scripts/Gui/CreateProfileScreen.as b/Resources/Scripts/Gui/MainScreen/CreateProfileScreen.as similarity index 100% rename from Resources/Scripts/Gui/CreateProfileScreen.as rename to Resources/Scripts/Gui/MainScreen/CreateProfileScreen.as diff --git a/Resources/Scripts/Gui/MainScreen/MainMenu.as b/Resources/Scripts/Gui/MainScreen/MainMenu.as new file mode 100644 index 00000000..81cd0166 --- /dev/null +++ b/Resources/Scripts/Gui/MainScreen/MainMenu.as @@ -0,0 +1,519 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "CreateProfileScreen.as" +#include "ServerList.as" + +namespace spades { + + class RefreshButton: spades::ui::SimpleButton { + RefreshButton(spades::ui::UIManager@ manager){ + super(manager); + } + void Render() { + SimpleButton::Render(); + + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/UI/Refresh.png"); + renderer.DrawImage(img, pos + (size - Vector2(16.f, 16.f)) * 0.5f); + } + } + + class ProtocolButton: spades::ui::SimpleButton { + ProtocolButton(spades::ui::UIManager@ manager){ + super(manager); + Toggle = true; + } + } + + uint8 ToLower(uint8 c) { + if(c >= uint8(0x41) and c <= uint8(0x5a)) { + return uint8(c - 0x41 + 0x61); + } else { + return c; + } + } + bool StringContainsCaseInsensitive(string text, string pattern) { + for(int i = text.length - 1; i >= 0; i--) + text[i] = ToLower(text[i]); + for(int i = pattern.length - 1; i >= 0; i--) + pattern[i] = ToLower(pattern[i]); + return text.findFirst(pattern) >= 0; + } + + class MainScreenMainMenu: spades::ui::UIElement { + + MainScreenUI@ ui; + MainScreenHelper@ helper; + spades::ui::Field@ addressField; + + spades::ui::Button@ protocol3Button; + spades::ui::Button@ protocol4Button; + + spades::ui::Button@ filterProtocol3Button; + spades::ui::Button@ filterProtocol4Button; + spades::ui::Button@ filterEmptyButton; + spades::ui::Button@ filterFullButton; + spades::ui::Field@ filterField; + + spades::ui::ListView@ serverList; + MainScreenServerListLoadingView@ loadingView; + MainScreenServerListErrorView@ errorView; + bool loading = false, loaded = false; + + private ConfigItem cg_protocolVersion("cg_protocolVersion", "3"); + private ConfigItem cg_lastQuickConnectHost("cg_lastQuickConnectHost", "127.0.0.1"); + private ConfigItem cg_serverlistSort("cg_serverlistSort", "16385"); + + MainScreenMainMenu(MainScreenUI@ ui) { + super(ui.manager); + @this.ui = ui; + @this.helper = ui.helper; + + float contentsWidth = 750.f; + float contentsLeft = (Manager.Renderer.ScreenWidth - contentsWidth) * 0.5f; + float footerPos = Manager.Renderer.ScreenHeight - 50.f; + { + spades::ui::Button button(Manager); + button.Caption = _Tr("MainScreen", "Connect"); + button.Bounds = AABB2(contentsLeft + contentsWidth - 150.f, 200.f, 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnConnectPressed); + AddChild(button); + } + { + @addressField = spades::ui::Field(Manager); + addressField.Bounds = AABB2(contentsLeft, 200, contentsWidth - 240.f, 30.f); + addressField.Placeholder = _Tr("MainScreen", "Quick Connect"); + addressField.Text = cg_lastQuickConnectHost.StringValue; + @addressField.Changed = spades::ui::EventHandler(this.OnAddressChanged); + AddChild(addressField); + } + { + @protocol3Button = ProtocolButton(Manager); + protocol3Button.Bounds = AABB2(contentsLeft + contentsWidth - 240.f + 6.f, 200, + 40.f, 30.f); + protocol3Button.Caption = _Tr("MainScreen", "0.75"); + @protocol3Button.Activated = spades::ui::EventHandler(this.OnProtocol3Pressed); + protocol3Button.Toggle = true; + protocol3Button.Toggled = cg_protocolVersion.IntValue == 3; + AddChild(protocol3Button); + } + { + @protocol4Button = ProtocolButton(Manager); + protocol4Button.Bounds = AABB2(contentsLeft + contentsWidth - 200.f + 6.f, 200, + 40.f, 30.f); + protocol4Button.Caption = _Tr("MainScreen", "0.76"); + @protocol4Button.Activated = spades::ui::EventHandler(this.OnProtocol4Pressed); + protocol4Button.Toggle = true; + protocol4Button.Toggled = cg_protocolVersion.IntValue == 4; + AddChild(protocol4Button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("MainScreen", "Quit"); + button.Bounds = AABB2(contentsLeft + contentsWidth - 100.f, footerPos, 100.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnQuitPressed); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("MainScreen", "Credits"); + button.Bounds = AABB2(contentsLeft + contentsWidth - 202.f, footerPos, 100.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnCreditsPressed); + AddChild(button); + } + { + spades::ui::Button button(Manager); + button.Caption = _Tr("MainScreen", "Setup"); + button.Bounds = AABB2(contentsLeft + contentsWidth - 304.f, footerPos, 100.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnSetupPressed); + AddChild(button); + } + { + RefreshButton button(Manager); + button.Bounds = AABB2(contentsLeft + contentsWidth - 364.f, footerPos, 30.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnRefreshServerListPressed); + AddChild(button); + } + { + spades::ui::Label label(Manager); + label.Text = _Tr("MainScreen", "Filter"); + label.Bounds = AABB2(contentsLeft, footerPos, 50.f, 30.f); + label.Alignment = Vector2(0.f, 0.5f); + AddChild(label); + } + { + @filterProtocol3Button = ProtocolButton(Manager); + filterProtocol3Button.Bounds = AABB2(contentsLeft + 50.f, footerPos, + 40.f, 30.f); + filterProtocol3Button.Caption = _Tr("MainScreen", "0.75"); + @filterProtocol3Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol3Pressed); + filterProtocol3Button.Toggle = true; + AddChild(filterProtocol3Button); + } + { + @filterProtocol4Button = ProtocolButton(Manager); + filterProtocol4Button.Bounds = AABB2(contentsLeft + 90.f, footerPos, + 40.f, 30.f); + filterProtocol4Button.Caption = _Tr("MainScreen", "0.76"); + @filterProtocol4Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol4Pressed); + filterProtocol4Button.Toggle = true; + AddChild(filterProtocol4Button); + } + { + @filterEmptyButton = ProtocolButton(Manager); + filterEmptyButton.Bounds = AABB2(contentsLeft + 135.f, footerPos, + 50.f, 30.f); + filterEmptyButton.Caption = _Tr("MainScreen", "Empty"); + @filterEmptyButton.Activated = spades::ui::EventHandler(this.OnFilterEmptyPressed); + filterEmptyButton.Toggle = true; + AddChild(filterEmptyButton); + } + { + @filterFullButton = ProtocolButton(Manager); + filterFullButton.Bounds = AABB2(contentsLeft + 185.f, footerPos, + 70.f, 30.f); + filterFullButton.Caption = _Tr("MainScreen", "Not Full"); + @filterFullButton.Activated = spades::ui::EventHandler(this.OnFilterFullPressed); + filterFullButton.Toggle = true; + AddChild(filterFullButton); + } + { + @filterField = spades::ui::Field(Manager); + filterField.Bounds = AABB2(contentsLeft + 260.f, footerPos, 120.f, 30.f); + filterField.Placeholder = _Tr("MainScreen", "Filter"); + @filterField.Changed = spades::ui::EventHandler(this.OnFilterTextChanged); + AddChild(filterField); + } + { + @serverList = spades::ui::ListView(Manager); + serverList.Bounds = AABB2(contentsLeft, 270.f, contentsWidth, footerPos - 280.f); + AddChild(serverList); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 2.f, 240.f, 300.f - 2.f, 30.f); + header.Text = _Tr("MainScreen", "Server Name"); + @header.Activated = spades::ui::EventHandler(this.SortServerListByName); + AddChild(header); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 300.f, 240.f, 100.f, 30.f); + header.Text = _Tr("MainScreen", "Players"); + @header.Activated = spades::ui::EventHandler(this.SortServerListByNumPlayers); + AddChild(header); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 400.f, 240.f, 150.f, 30.f); + header.Text = _Tr("MainScreen", "Map Name"); + @header.Activated = spades::ui::EventHandler(this.SortServerListByMapName); + AddChild(header); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 550.f, 240.f, 80.f, 30.f); + header.Text = _Tr("MainScreen", "Game Mode"); + @header.Activated = spades::ui::EventHandler(this.SortServerListByGameMode); + AddChild(header); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 630.f, 240.f, 50.f, 30.f); + header.Text = _Tr("MainScreen", "Ver."); + @header.Activated = spades::ui::EventHandler(this.SortServerListByProtocol); + AddChild(header); + } + { + ServerListHeader header(Manager); + header.Bounds = AABB2(contentsLeft + 680.f, 240.f, 50.f, 30.f); + header.Text = _Tr("MainScreen", "Loc."); + @header.Activated = spades::ui::EventHandler(this.SortServerListByCountry); + AddChild(header); + } + { + @loadingView = MainScreenServerListLoadingView(Manager); + loadingView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f); + loadingView.Visible = false; + AddChild(loadingView); + } + { + @errorView = MainScreenServerListErrorView(Manager); + errorView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f); + errorView.Visible = false; + AddChild(errorView); + } + LoadServerList(); + } + + void LoadServerList() { + if(loading) { + return; + } + loaded = false; + loading = true; + @serverList.Model = spades::ui::ListViewModel(); // empty + errorView.Visible = false; + loadingView.Visible = true; + helper.StartQuery(); + } + + void ServerListItemActivated(ServerListModel@ sender, MainScreenServerItem@ item) { + addressField.Text = item.Address; + cg_lastQuickConnectHost = addressField.Text; + if(item.Protocol == "0.75") { + SetProtocolVersion(3); + }else if(item.Protocol == "0.76") { + SetProtocolVersion(4); + } + addressField.SelectAll(); + } + + void ServerListItemDoubleClicked(ServerListModel@ sender, MainScreenServerItem@ item) { + ServerListItemActivated(sender, item); + + // Double-click to connect + Connect(); + } + + void ServerListItemRightClicked(ServerListModel@ sender, MainScreenServerItem@ item) { + helper.SetServerFavorite(item.Address, !item.Favorite); + UpdateServerList(); + } + + private void SortServerListByPing(spades::ui::UIElement@ sender) { + SortServerList(0); + } + private void SortServerListByNumPlayers(spades::ui::UIElement@ sender) { + SortServerList(1); + } + private void SortServerListByName(spades::ui::UIElement@ sender) { + SortServerList(2); + } + private void SortServerListByMapName(spades::ui::UIElement@ sender) { + SortServerList(3); + } + private void SortServerListByGameMode(spades::ui::UIElement@ sender) { + SortServerList(4); + } + private void SortServerListByProtocol(spades::ui::UIElement@ sender) { + SortServerList(5); + } + private void SortServerListByCountry(spades::ui::UIElement@ sender) { + SortServerList(6); + } + + private void SortServerList(int keyId) { + int sort = cg_serverlistSort.IntValue; + if(int(sort & 0xfff) == keyId) { + sort ^= int(0x4000); + } else { + sort = keyId; + } + cg_serverlistSort = sort; + UpdateServerList(); + } + + private void UpdateServerList() { + string key = ""; + switch(cg_serverlistSort.IntValue & 0xfff) { + case 0: key = "Ping"; break; + case 1: key = "NumPlayers"; break; + case 2: key = "Name"; break; + case 3: key = "MapName"; break; + case 4: key = "GameMode"; break; + case 5: key = "Protocol"; break; + case 6: key = "Country"; break; + } + MainScreenServerItem@[]@ list = helper.GetServerList(key, + (cg_serverlistSort.IntValue & 0x4000) != 0); + if((list is null) or (loading)){ + @serverList.Model = spades::ui::ListViewModel(); // empty + return; + } + + // filter the server list + bool filterProtocol3 = filterProtocol3Button.Toggled; + bool filterProtocol4 = filterProtocol4Button.Toggled; + bool filterEmpty = filterEmptyButton.Toggled; + bool filterFull = filterFullButton.Toggled; + string filterText = filterField.Text; + MainScreenServerItem@[]@ list2 = array(); + for(int i = 0, count = list.length; i < count; i++) { + MainScreenServerItem@ item = list[i]; + if(filterProtocol3 and (item.Protocol != "0.75")) { + continue; + } + if(filterProtocol4 and (item.Protocol != "0.76")) { + continue; + } + if(filterEmpty and (item.NumPlayers > 0)) { + continue; + } + if(filterFull and (item.NumPlayers >= item.MaxPlayers)) { + continue; + } + if(filterText.length > 0) { + if(not (StringContainsCaseInsensitive(item.Name, filterText) or + StringContainsCaseInsensitive(item.MapName, filterText) or + StringContainsCaseInsensitive(item.GameMode, filterText))) { + continue; + } + } + list2.insertLast(item); + } + + ServerListModel model(Manager, list2); + @serverList.Model = model; + @model.ItemActivated = ServerListItemEventHandler(this.ServerListItemActivated); + @model.ItemDoubleClicked = ServerListItemEventHandler(this.ServerListItemDoubleClicked); + @model.ItemRightClicked = ServerListItemEventHandler(this.ServerListItemRightClicked); + serverList.ScrollToTop(); + } + + private void CheckServerList() { + if(helper.PollServerListState()) { + MainScreenServerItem@[]@ list = helper.GetServerList("", false); + if(list is null or list.length == 0) { + // failed. + // FIXME: show error message? + loaded = false; loading = false; + errorView.Visible = true; + loadingView.Visible = false; + @serverList.Model = spades::ui::ListViewModel(); // empty + return; + } + loading = false; + loaded = true; + errorView.Visible = false; + loadingView.Visible = false; + UpdateServerList(); + } + } + + private void OnAddressChanged(spades::ui::UIElement@ sender) { + cg_lastQuickConnectHost = addressField.Text; + } + + private void SetProtocolVersion(int ver) { + protocol3Button.Toggled = (ver == 3); + protocol4Button.Toggled = (ver == 4); + cg_protocolVersion = ver; + } + + private void OnProtocol3Pressed(spades::ui::UIElement@ sender) { + SetProtocolVersion(3); + } + + private void OnProtocol4Pressed(spades::ui::UIElement@ sender) { + SetProtocolVersion(4); + } + + private void OnFilterProtocol3Pressed(spades::ui::UIElement@ sender) { + filterProtocol4Button.Toggled = false; + UpdateServerList(); + } + private void OnFilterProtocol4Pressed(spades::ui::UIElement@ sender) { + filterProtocol3Button.Toggled = false; + UpdateServerList(); + } + private void OnFilterFullPressed(spades::ui::UIElement@ sender) { + filterEmptyButton.Toggled = false; + UpdateServerList(); + } + private void OnFilterEmptyPressed(spades::ui::UIElement@ sender) { + filterFullButton.Toggled = false; + UpdateServerList(); + } + private void OnFilterTextChanged(spades::ui::UIElement@ sender) { + UpdateServerList(); + } + + private void OnRefreshServerListPressed(spades::ui::UIElement@ sender) { + LoadServerList(); + } + + private void OnQuitPressed(spades::ui::UIElement@ sender) { + ui.shouldExit = true; + } + + private void OnCreditsPressed(spades::ui::UIElement@ sender) { + AlertScreen al(this, ui.helper.Credits, Min(500.f, Manager.Renderer.ScreenHeight - 100.f)); + al.Run(); + } + + private void OnSetupPressed(spades::ui::UIElement@ sender) { + PreferenceView al(this, PreferenceViewOptions(), ui.fontManager); + al.Run(); + } + + private void Connect() { + string msg = helper.ConnectServer(addressField.Text, cg_protocolVersion.IntValue); + if(msg.length > 0) { + // failde to initialize client. + AlertScreen al(this, msg); + al.Run(); + } + } + + private void OnConnectPressed(spades::ui::UIElement@ sender) { + Connect(); + } + + void HotKey(string key) { + if(IsEnabled and key == "Enter") { + Connect(); + } else if(IsEnabled and key == "Escape") { + ui.shouldExit = true; + } else { + UIElement::HotKey(key); + } + } + + void Render() { + CheckServerList(); + UIElement::Render(); + + // check for client error message. + if(IsEnabled) { + string msg = helper.GetPendingErrorMessage(); + if(msg.length > 0) { + // try to maek the "disconnected" message more friendly. + if(msg.findFirst("Disconnected:") >= 0) { + int ind1 = msg.findFirst("Disconnected:"); + int ind2 = msg.findFirst("\n", ind1); + if(ind2 < 0) ind2 = msg.length; + ind1 += "Disconnected:".length; + msg = msg.substr(ind1, ind2 - ind1); + msg = _Tr("MainScreen", "You were disconnected from the server because of the following reason:\n\n{0}", msg); + } + + // failed to connect. + AlertScreen al(this, msg); + al.Run(); + } + } + } + } + +} diff --git a/Resources/Scripts/Gui/MainScreen/MainScreenUI.as b/Resources/Scripts/Gui/MainScreen/MainScreenUI.as new file mode 100644 index 00000000..afc17d5f --- /dev/null +++ b/Resources/Scripts/Gui/MainScreen/MainScreenUI.as @@ -0,0 +1,195 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "MainMenu.as" +#include "CreateProfileScreen.as" + +namespace spades { + + class MainScreenUI { + private Renderer@ renderer; + private AudioDevice@ audioDevice; + FontManager@ fontManager; + MainScreenHelper@ helper; + + spades::ui::UIManager@ manager; + + MainScreenMainMenu@ mainMenu; + + bool shouldExit = false; + + private float time = -1.f; + + private ConfigItem cg_playerName("cg_playerName"); + private ConfigItem cg_playerNameIsSet("cg_playerNameIsSet", "0"); + + MainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, MainScreenHelper@ helper) { + @this.renderer = renderer; + @this.audioDevice = audioDevice; + @this.fontManager = fontManager; + @this.helper = helper; + + SetupRenderer(); + + @manager = spades::ui::UIManager(renderer, audioDevice); + @manager.RootElement.Font = fontManager.GuiFont; + + @mainMenu = MainScreenMainMenu(this); + mainMenu.Bounds = manager.RootElement.Bounds; + manager.RootElement.AddChild(mainMenu); + + // Let the new player choose their IGN + if (cg_playerName.StringValue != "" && + cg_playerName.StringValue != "Deuce") { + cg_playerNameIsSet.IntValue = 1; + } + if (cg_playerNameIsSet.IntValue == 0) { + CreateProfileScreen al(mainMenu); + al.Run(); + } + + } + + void SetupRenderer() { + // load map + @renderer.GameMap = GameMap("Maps/Title.vxl"); + renderer.FogColor = Vector3(0.1f, 0.10f, 0.1f); + renderer.FogDistance = 128.f; + time = -1.f; + + // returned from the client game, so reload the server list. + if(mainMenu !is null) + mainMenu.LoadServerList(); + + if(manager !is null) + manager.KeyPanic(); + } + + void MouseEvent(float x, float y) { + manager.MouseEvent(x, y); + } + + void WheelEvent(float x, float y) { + manager.WheelEvent(x, y); + } + + void KeyEvent(string key, bool down) { + manager.KeyEvent(key, down); + } + + void TextInputEvent(string text) { + manager.TextInputEvent(text); + } + + void TextEditingEvent(string text, int start, int len) { + manager.TextEditingEvent(text, start, len); + } + + bool AcceptsTextInput() { + return manager.AcceptsTextInput; + } + + AABB2 GetTextInputRect() { + return manager.TextInputRect; + } + + private SceneDefinition SetupCamera(SceneDefinition sceneDef, + Vector3 eye, Vector3 at, Vector3 up, float fov) { + Vector3 dir = (at - eye).Normalized; + Vector3 side = Cross(dir, up).Normalized; + up = -Cross(dir, side); + sceneDef.viewOrigin = eye; + sceneDef.viewAxisX = side; + sceneDef.viewAxisY = up; + sceneDef.viewAxisZ = dir; + sceneDef.fovY = fov * 3.141592654f / 180.f; + sceneDef.fovX = atan(tan(sceneDef.fovY * 0.5f) * renderer.ScreenWidth / renderer.ScreenHeight) * 2.f; + return sceneDef; + } + + void RunFrame(float dt) { + if(time < 0.f) { + time = 0.f; + } + + SceneDefinition sceneDef; + float cameraX = time; + cameraX -= floor(cameraX / 512.f) * 512.f; + cameraX = 512.f - cameraX; + sceneDef = SetupCamera(sceneDef, + Vector3(cameraX, 256.f, 12.f), Vector3(cameraX + .1f, 257.f, 12.5f), Vector3(0.f, 0.f, -1.f), + 30.f); + sceneDef.zNear = 0.1f; + sceneDef.zFar = 222.f; + sceneDef.time = int(time * 1000.f); + sceneDef.viewportWidth = int(renderer.ScreenWidth); + sceneDef.viewportHeight = int(renderer.ScreenHeight); + sceneDef.denyCameraBlur = true; + sceneDef.depthOfFieldFocalLength = 100.f; + sceneDef.skipWorld = false; + + // fade the map + float fade = Clamp((time - 1.f) / 2.2f, 0.f, 1.f); + sceneDef.globalBlur = Clamp((1.f - (time - 1.f) / 2.5f), 0.f, 1.f); + if(!mainMenu.IsEnabled) { + sceneDef.globalBlur = Max(sceneDef.globalBlur, 0.5f); + } + + renderer.StartScene(sceneDef); + renderer.EndScene(); + + // fade the map (draw) + if(fade < 1.f) { + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 1.f - fade); + renderer.DrawImage(renderer.RegisterImage("Gfx/White.tga"), + AABB2(0.f, 0.f, renderer.ScreenWidth, renderer.ScreenHeight)); + } + + // draw title logo + Image@ img = renderer.RegisterImage("Gfx/Title/Logo.png"); + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 1.f); + renderer.DrawImage(img, Vector2((renderer.ScreenWidth - img.Width) * 0.5f, 64.f)); + + manager.RunFrame(dt); + manager.Render(); + + renderer.FrameDone(); + renderer.Flip(); + time += Min(dt, 0.05f); + } + + void Closing() { + shouldExit = true; + } + + bool WantsToBeClosed() { + return shouldExit; + } + } + + /** + * The entry point of the main screen. + */ + MainScreenUI@ CreateMainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, + FontManager@ fontManager, MainScreenHelper@ helper) { + return MainScreenUI(renderer, audioDevice, fontManager, helper); + } + +} diff --git a/Resources/Scripts/Gui/MainScreen/ServerList.as b/Resources/Scripts/Gui/MainScreen/ServerList.as new file mode 100644 index 00000000..1ede2855 --- /dev/null +++ b/Resources/Scripts/Gui/MainScreen/ServerList.as @@ -0,0 +1,177 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "CountryFlags.as" + +namespace spades { + + class ServerListItem: spades::ui::ButtonBase { + MainScreenServerItem@ item; + FlagIconRenderer@ flagIconRenderer; + ServerListItem(spades::ui::UIManager@ manager, MainScreenServerItem@ item){ + super(manager); + @this.item = item; + @flagIconRenderer = FlagIconRenderer(manager.Renderer); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + + Vector4 bgcolor = Vector4(1.f, 1.f, 1.f, 0.0f); + Vector4 fgcolor = Vector4(1.f, 1.f, 1.f, 1.f); + if(item.Favorite) { + bgcolor = Vector4(0.3f, 0.3f, 1.f, 0.1f); + fgcolor = Vector4(220.f/255.f,220.f/255.f,0,1); + } + if(Pressed && Hover) { + bgcolor.w += 0.3; + } else if(Hover) { + bgcolor.w += 0.15; + } + renderer.ColorNP = bgcolor; + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + + Font.Draw(item.Name, ScreenPosition + Vector2(4.f, 2.f), 1.f, fgcolor); + string playersStr = ToString(item.NumPlayers) + "/" + ToString(item.MaxPlayers); + Vector4 col(1,1,1,1); + if(item.NumPlayers >= item.MaxPlayers) col = Vector4(1,0.7f,0.7f,1); + else if(item.NumPlayers >= item.MaxPlayers * 3 / 4) col = Vector4(1,1,0.7f,1); + else if(item.NumPlayers == 0) col = Vector4(0.7f,0.7f,1,1); + Font.Draw(playersStr, ScreenPosition + Vector2(340.f-Font.Measure(playersStr).x * 0.5f, 2.f), 1.f, col); + Font.Draw(item.MapName, ScreenPosition + Vector2(400.f, 2.f), 1.f, Vector4(1,1,1,1)); + Font.Draw(item.GameMode, ScreenPosition + Vector2(550.f, 2.f), 1.f, Vector4(1,1,1,1)); + Font.Draw(item.Protocol, ScreenPosition + Vector2(630.f, 2.f), 1.f, Vector4(1,1,1,1)); + if(not flagIconRenderer.DrawIcon(item.Country, ScreenPosition + Vector2(700.f, size.y * 0.5f))) { + Font.Draw(item.Country, ScreenPosition + Vector2(680.f, 2.f), 1.f, Vector4(1,1,1,1)); + } + } + } + + funcdef void ServerListItemEventHandler(ServerListModel@ sender, MainScreenServerItem@ item); + + class ServerListModel: spades::ui::ListViewModel { + spades::ui::UIManager@ manager; + MainScreenServerItem@[]@ list; + + ServerListItemEventHandler@ ItemActivated; + ServerListItemEventHandler@ ItemDoubleClicked; + ServerListItemEventHandler@ ItemRightClicked; + + ServerListModel(spades::ui::UIManager@ manager, MainScreenServerItem@[]@ list) { + @this.manager = manager; + @this.list = list; + } + int NumRows { + get { return int(list.length); } + } + private void OnItemClicked(spades::ui::UIElement@ sender){ + ServerListItem@ item = cast(sender); + if(ItemActivated !is null) { + ItemActivated(this, item.item); + } + } + private void OnItemDoubleClicked(spades::ui::UIElement@ sender){ + ServerListItem@ item = cast(sender); + if(ItemDoubleClicked !is null) { + ItemDoubleClicked(this, item.item); + } + } + private void OnItemRightClicked(spades::ui::UIElement@ sender){ + ServerListItem@ item = cast(sender); + if(ItemRightClicked !is null) { + ItemRightClicked(this, item.item); + } + } + spades::ui::UIElement@ CreateElement(int row) { + ServerListItem i(manager, list[row]); + @i.Activated = spades::ui::EventHandler(this.OnItemClicked); + @i.DoubleClicked = spades::ui::EventHandler(this.OnItemDoubleClicked); + @i.RightClicked = spades::ui::EventHandler(this.OnItemRightClicked); + return i; + } + void RecycleElement(spades::ui::UIElement@ elem) {} + } + + + class ServerListHeader: spades::ui::ButtonBase { + string Text; + ServerListHeader(spades::ui::UIManager@ manager){ + super(manager); + } + void OnActivated() { + ButtonBase::OnActivated(); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + if(Pressed && Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.15f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.0f); + } + renderer.DrawImage(img, AABB2(pos.x - 2.f, pos.y, size.x, size.y)); + + Font.Draw(Text, ScreenPosition + Vector2(0.f, 2.f), 1.f, Vector4(1,1,1,1)); + } + } + + class MainScreenServerListLoadingView: spades::ui::UIElement { + MainScreenServerListLoadingView(spades::ui::UIManager@ manager) { + super(manager); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Font@ font = this.Font; + string text = _Tr("MainScreen", "Loading..."); + Vector2 txtSize = font.Measure(text); + Vector2 txtPos; + txtPos = pos + (size - txtSize) * 0.5f; + + font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8)); + } + } + + class MainScreenServerListErrorView: spades::ui::UIElement { + MainScreenServerListErrorView(spades::ui::UIManager@ manager) { + super(manager); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Font@ font = this.Font; + string text = _Tr("MainScreen", "Failed to fetch the server list."); + Vector2 txtSize = font.Measure(text); + Vector2 txtPos; + txtPos = pos + (size - txtSize) * 0.5f; + + font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8)); + } + } + +} diff --git a/Resources/Scripts/Gui/StartupScreen.as b/Resources/Scripts/Gui/StartupScreen.as deleted file mode 100644 index 3ad70568..00000000 --- a/Resources/Scripts/Gui/StartupScreen.as +++ /dev/null @@ -1,1906 +0,0 @@ -/* - Copyright (c) 2013 yvt - - This file is part of OpenSpades. - - OpenSpades is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenSpades is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OpenSpades. If not, see . - - */ - -#include "DropDownList.as" -#include "MessageBox.as" -#include "UpdateCheckView.as" - -namespace spades { - - - class StartupScreenUI { - private Renderer@ renderer; - private AudioDevice@ audioDevice; - FontManager@ fontManager; - StartupScreenHelper@ helper; - - spades::ui::UIManager@ manager; - - StartupScreenMainMenu@ mainMenu; - - bool shouldExit = false; - - StartupScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, StartupScreenHelper@ helper) { - @this.renderer = renderer; - @this.audioDevice = audioDevice; - @this.fontManager = fontManager; - @this.helper = helper; - - SetupRenderer(); - - @manager = spades::ui::UIManager(renderer, audioDevice); - @manager.RootElement.Font = fontManager.GuiFont; - Init(); - } - - private void Init() { - @mainMenu = StartupScreenMainMenu(this); - mainMenu.Bounds = manager.RootElement.Bounds; - manager.RootElement.AddChild(mainMenu); - } - - void Reload() { - // Delete StartupScreenMainMenu - @manager.RootElement.GetChildren()[0].Parent = null; - - // Reload entire the startup screen while preserving the state as much as possible - auto@ state = mainMenu.GetState(); - Init(); - mainMenu.SetState(state); - } - - void SetupRenderer() { - if(manager !is null) - manager.KeyPanic(); - } - - void MouseEvent(float x, float y) { - manager.MouseEvent(x, y); - } - - void WheelEvent(float x, float y) { - manager.WheelEvent(x, y); - } - - void KeyEvent(string key, bool down) { - manager.KeyEvent(key, down); - } - - void TextInputEvent(string text) { - manager.TextInputEvent(text); - } - - void TextEditingEvent(string text, int start, int len) { - manager.TextEditingEvent(text, start, len); - } - - bool AcceptsTextInput() { - return manager.AcceptsTextInput; - } - - AABB2 GetTextInputRect() { - return manager.TextInputRect; - } - - void RunFrame(float dt) { - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 1.f); - renderer.DrawImage(renderer.RegisterImage("Gfx/White.tga"), - AABB2(0.f, 0.f, renderer.ScreenWidth, renderer.ScreenHeight)); - - // draw title logo - Image@ img = renderer.RegisterImage("Gfx/Title/LogoSmall.png"); - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 1.f); - renderer.DrawImage(img, AABB2(10.f, 10.f, img.Width, img.Height)); - - manager.RunFrame(dt); - manager.Render(); - - renderer.FrameDone(); - renderer.Flip(); - } - - void Closing() { - shouldExit = true; - } - - bool WantsToBeClosed() { - return shouldExit; - } - } - - class StartupScreenMainMenuState { - int ActiveTabIndex; - } - - class StartupScreenMainMenu: spades::ui::UIElement { - - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - spades::ui::ListView@ serverList; - spades::ui::CheckBox@ bypassStartupWindowCheck; - - StartupScreenGraphicsTab@ graphicsTab; - StartupScreenAudioTab@ audioTab; - StartupScreenGenericTab@ genericTab; - StartupScreenSystemInfoTab@ systemInfoTab; - StartupScreenAdvancedTab@ advancedTab; - - private ConfigItem cl_showStartupWindow("cl_showStartupWindow", "-1"); - private bool advancedTabVisible = false; - - StartupScreenMainMenu(StartupScreenUI@ ui) { - super(ui.manager); - @this.ui = ui; - @this.helper = ui.helper; - - @this.Font = ui.fontManager.GuiFont; - - float width = Manager.Renderer.ScreenWidth; - float height = Manager.Renderer.ScreenHeight; - { - spades::ui::Button button(Manager); - button.Caption = _Tr("StartupScreen", "Start"); - button.Bounds = AABB2(width - 170.f, 20.f, 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnStartPressed); - AddChild(button); - } - { - spades::ui::CheckBox button(Manager); - button.Caption = _Tr("StartupScreen", "Skip this screen next time"); - button.Bounds = AABB2(360.f, 62.f, width - 380.f, 20.f); // note: this is updated later soon - AddChild(button); - @bypassStartupWindowCheck = button; - @button.Activated = spades::ui::EventHandler(this.OnBypassStartupWindowCheckChanged); - } - { - UpdateCheckView view(Manager, ui.helper.PackageUpdateManager); - view.Bounds = AABB2(0.f, height - 40.f, width, 40.f); - @view.OpenUpdateInfoURL = spades::ui::EventHandler(this.OnShowUpdateDetailsPressed); - AddChild(view); - } - - AABB2 clientArea(10.f, 100.f, width - 20.f, height - 150.f); - StartupScreenGraphicsTab graphicsTab(ui, clientArea.max - clientArea.min); - StartupScreenAudioTab audioTab(ui, clientArea.max - clientArea.min); - StartupScreenGenericTab genericTab(ui, clientArea.max - clientArea.min); - StartupScreenSystemInfoTab profileTab(ui, clientArea.max - clientArea.min); - StartupScreenAdvancedTab advancedTab(ui, clientArea.max - clientArea.min); - graphicsTab.Bounds = clientArea; - audioTab.Bounds = clientArea; - genericTab.Bounds = clientArea; - profileTab.Bounds = clientArea; - advancedTab.Bounds = clientArea; - AddChild(graphicsTab); - AddChild(audioTab); - AddChild(genericTab); - AddChild(profileTab); - AddChild(advancedTab); - audioTab.Visible = false; - profileTab.Visible = false; - genericTab.Visible = false; - advancedTab.Visible = false; - - @this.graphicsTab = graphicsTab; - @this.audioTab = audioTab; - @this.advancedTab = advancedTab; - @this.systemInfoTab = profileTab; - @this.genericTab = genericTab; - - { - spades::ui::SimpleTabStrip tabStrip(Manager); - AddChild(tabStrip); - tabStrip.Bounds = AABB2(10.f, 70.f, width - 20.f, 24.f); - tabStrip.AddItem(_Tr("StartupScreen", "Graphics"), graphicsTab); - tabStrip.AddItem(_Tr("StartupScreen", "Audio"), audioTab); - tabStrip.AddItem(_Tr("StartupScreen", "Generic"), genericTab); - tabStrip.AddItem(_Tr("StartupScreen", "System Info"), profileTab); - tabStrip.AddItem(_Tr("StartupScreen", "Advanced"), advancedTab); - @tabStrip.Changed = spades::ui::EventHandler(this.OnTabChanged); - - // Reposition the "Skip this screen next time" check box - spades::ui::UIElement@[]@ tabStripItems = tabStrip.GetChildren(); - float right = tabStripItems[tabStripItems.length - 1].Bounds.max.x + - tabStrip.Bounds.min.x + 10.f; - bypassStartupWindowCheck.Bounds = AABB2(right, 62.f, width - right - 20.f, 20.f); - } - - LoadConfig(); - } - - private void OnTabChanged(spades::ui::UIElement@) { - bool v = advancedTab.Visible; - if(advancedTabVisible and not v) { - LoadConfig(); - } - advancedTabVisible = v; - } - - void LoadConfig() { - switch(cl_showStartupWindow.IntValue) { - case -1: - bypassStartupWindowCheck.Toggled = false; - break; - case 0: - bypassStartupWindowCheck.Toggled = true; - break; - default: - bypassStartupWindowCheck.Toggled = false; - break; - } - - this.graphicsTab.LoadConfig(); - this.audioTab.LoadConfig(); - this.genericTab.LoadConfig(); - this.advancedTab.LoadConfig(); - } - - StartupScreenMainMenuState@ GetState() { - StartupScreenMainMenuState state; - if (this.graphicsTab.Visible) { - state.ActiveTabIndex = 0; - } else if (this.audioTab.Visible) { - state.ActiveTabIndex = 1; - } else if (this.genericTab.Visible) { - state.ActiveTabIndex = 2; - } else if (this.systemInfoTab.Visible) { - state.ActiveTabIndex = 3; - } else if (this.advancedTab.Visible) { - state.ActiveTabIndex = 4; - } - return state; - } - - void SetState(StartupScreenMainMenuState@ state) { - this.graphicsTab.Visible = state.ActiveTabIndex == 0; - this.audioTab.Visible = state.ActiveTabIndex == 1; - this.genericTab.Visible = state.ActiveTabIndex == 2; - this.systemInfoTab.Visible = state.ActiveTabIndex == 3; - this.advancedTab.Visible = state.ActiveTabIndex == 4; - } - - private void OnBypassStartupWindowCheckChanged(spades::ui::UIElement@ sender) { - cl_showStartupWindow.IntValue = (bypassStartupWindowCheck.Toggled ? 0 : 1); - } - - private void OnShowUpdateDetailsPressed(spades::ui::UIElement@ sender) { - if (ui.helper.OpenUpdateInfoURL()) { - return; - } - - string msg = _Tr("StartupScreen", "An unknown error has occurred while opening the update info website."); - msg += "\n\n" + ui.helper.PackageUpdateManager.LatestVersionInfoPageURL; - AlertScreen al(Parent, msg, 100.f); - al.Run(); - } - - private void OnQuitPressed(spades::ui::UIElement@ sender) { - ui.shouldExit = true; - } - - private void OnSetupPressed(spades::ui::UIElement@ sender) { - PreferenceView al(this, PreferenceViewOptions(), ui.fontManager); - al.Run(); - } - - private void Start() { - helper.Start(); - ui.shouldExit = true; // we have to exit from the startup screen to start the game - } - - private void OnStartPressed(spades::ui::UIElement@ sender) { - Start(); - } - - void HotKey(string key) { - if(IsEnabled and key == "Enter") { - Start(); - } else if(IsEnabled and key == "Escape") { - ui.shouldExit = true; - } else { - UIElement::HotKey(key); - } - } - - void Render() { - UIElement::Render(); - - } - } - - funcdef void HelpTextHandler(string text); - class ChainedEventHandler { - spades::ui::EventHandler@ h; - spades::ui::EventHandler@ h2; - ChainedEventHandler(spades::ui::EventHandler@ h, spades::ui::EventHandler@ h2) { - @this.h = h; - @this.h2 = h2; - } - void Handler(spades::ui::UIElement@ e) { - h(e); h2(e); - } - } - class HelpHandler { - private HelpTextHandler@ handler; - private spades::ui::TextViewer@ helpView; - private string text; - HelpHandler(spades::ui::TextViewer@ helpView, string text) { - this.text = text; - @this.helpView = helpView; - } - HelpHandler(HelpTextHandler@ handler, string text) { - this.text = text; - @this.handler = handler; - } - private void OnMouseHover(spades::ui::UIElement@ elm) { - if(helpView !is null) { - helpView.Text = text; - } - if(handler !is null) { - handler(text); - } - } - void Watch(spades::ui::UIElement@ elm) { - @elm.MouseEntered = spades::ui::EventHandler(this.OnMouseHover); - } - void WatchDeep(spades::ui::UIElement@ elm) { - if(elm.MouseEntered !is null) { - ChainedEventHandler chain(elm.MouseEntered, spades::ui::EventHandler(this.OnMouseHover)); - @elm.MouseEntered = spades::ui::EventHandler(chain.Handler); - }else{ - Watch(elm); - } - spades::ui::UIElementIterator it(elm); - while(it.MoveNext()) { - WatchDeep(it.Current); - } - } - } - - mixin class LabelAddable { - - private void AddLabel(float x, float y, float h, string text) { - spades::ui::Label label(Manager); - Font@ font = ui.fontManager.GuiFont; - Vector2 siz = font.Measure(text); - label.Text = text; - label.Alignment = Vector2(0.f, 0.5f); - label.Bounds = AABB2(x, y, siz.x, h); - AddChild(label); - } - - } - - class StartupScreenConfigViewModel: spades::ui::ListViewModel { - spades::ui::UIElement@[] items; - spades::ui::UIElement@[]@ items2; - StartupScreenConfigViewModel() { - } - int NumRows { - get { - if(items2 !is null) - return int(items2.length); - return int(items.length); - } - } - - void Filter(string text) { - if(text.length == 0) { - @items2 = null; - return; - } - spades::ui::UIElement@[] newItems; - for(uint i = 0, count = items.length; i < count; i++) { - StartupScreenConfigItemEditor@ editor = - cast(items[i]); - if(editor is null) continue; - string label = editor.GetLabel(); - if(StringContainsCaseInsensitive(label, text)) { - newItems.insertLast(items[i]); - } - } - @items2 = newItems; - } - spades::ui::UIElement@ CreateElement(int row) { - if(items2 !is null) - return items2[row]; - return items[row]; - } - void RecycleElement(spades::ui::UIElement@ elem) { - } - } - - interface StartupScreenGenericConfig { - string GetValue(); - void SetValue(string); - /** Returns an empty string when there's no problem. */ - string CheckValueCapability(string); - } - - class StartupScreenConfig: StartupScreenGenericConfig { - private StartupScreenUI@ ui; - private ConfigItem@ cfg; - private string cfgName; - StartupScreenConfig(StartupScreenUI@ ui, string cfg) { - @this.ui = ui; - @this.cfg = ConfigItem(cfg); - cfgName = cfg; - } - string GetValue() { - return cfg.StringValue; - } - void SetValue(string v) { - cfg.StringValue = v; - } - string CheckValueCapability(string v) { - return ui.helper.CheckConfigCapability(cfgName, v); - } - } - - class StartupScreenConfigSetter { - StartupScreenGenericConfig@ c; - string value; - StartupScreenConfigSetter(StartupScreenGenericConfig@ c, string value) { - @this.c = c; - this.value = value; - } - void Set(spades::ui::UIElement@) { - c.SetValue(this.value); - } - } - - interface StartupScreenConfigItemEditor { - void LoadConfig(); - StartupScreenGenericConfig@ GetConfig(); - void SetHelpTextHandler(HelpTextHandler@); - string GetLabel(); - } - - - class StartupScreenConfigSelectItemEditor: spades::ui::UIElement, LabelAddable, StartupScreenConfigItemEditor, StartupScreenConfigItemEditor { - - protected StartupScreenUI@ ui; - private string[]@ descs; - private string[]@ values; - protected StartupScreenGenericConfig@ config; - protected spades::ui::RadioButton@[] buttons; - private string label; - - StartupScreenConfigSelectItemEditor(StartupScreenUI@ ui, - StartupScreenGenericConfig@ cfg, - string values, string descs) { - super(ui.manager); - @this.ui = ui; - @this.descs = descs.split("|"); - @config = cfg; - @this.values = values.split("|"); - - { - string desc = this.descs[0]; - int idx = desc.findFirst(":"); - if(idx >= 0) { - desc = desc.substr(0, idx); - } - AddLabel(0, 0, 24.f, desc); - this.label = desc; - } - - - for(uint i = 0; i < this.values.length; i++) { - spades::ui::RadioButton b(Manager); - string desc = this.descs[i + 1]; - int idx = desc.findFirst(":"); - if(idx >= 0) { - desc = desc.substr(0, idx); - } - b.Caption = desc; - - b.GroupName = "hoge"; - StartupScreenConfigSetter setter(config, this.values[i]); - @b.Activated = spades::ui::EventHandler(setter.Set); - buttons.insertLast(b); - this.AddChild(b); - } - } - - string GetLabel() { - return label; - } - - void LoadConfig() { - string val = config.GetValue(); - for(uint i = 0, count = values.length; i < count; i++) { - buttons[i].Toggled = (values[i] == val); - buttons[i].Enable = CheckValueCapability(values[i]).length == 0; - } - } - string CheckValueCapability(string v) { - return config.CheckValueCapability(v); - } - StartupScreenGenericConfig@ GetConfig() { - return config; - } - void SetHelpTextHandler(HelpTextHandler@ handler) { - for(uint i = 0, count = values.length; i < count; i++) { - string desc = descs[i + 1]; - int idx = desc.findFirst(":"); - if(idx < 0) { - desc = descs[0]; - idx = desc.findFirst(":"); - } - desc = desc.substr(uint(idx + 1)); - HelpHandler(handler, desc).Watch(buttons[i]); - } - } - - void set_Bounds(AABB2 v) { - UIElement::set_Bounds(v); - Vector2 size = this.Size; - float h = 24.f; - float x = size.x; - for(uint i = buttons.length; i > 0; i--) { - spades::ui::RadioButton@ b = buttons[i - 1]; - float w = ui.fontManager.GuiFont.Measure(b.Caption).x + 26.f; - x -= w + 2.f; - b.Bounds = AABB2(x, 0.f, w, h); - } - } - } - - class StartupScreenConfigCheckItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor { - - protected StartupScreenUI@ ui; - private string desc; - private string valueOff; - private string valueOn; - private StartupScreenGenericConfig@ config; - private spades::ui::CheckBox@ button; - private string label; - - StartupScreenConfigCheckItemEditor(StartupScreenUI@ ui, - StartupScreenGenericConfig@ cfg, - string valueOff, string valueOn, string label, string desc) { - super(ui.manager); - @this.ui = ui; - @config = cfg; - this.valueOff = valueOff; - this.valueOn = valueOn; - this.desc = desc; - - spades::ui::CheckBox b(Manager); - b.Caption = label; - this.label = label; - @b.Activated = spades::ui::EventHandler(this.StateChanged); - @button = b; - this.AddChild(b); - } - - string GetLabel() { - return label; - } - - void LoadConfig() { - string val = config.GetValue(); - button.Toggled = (val != valueOff); - button.Enable = CheckValueCapability(valueOn).length == 0; - } - string CheckValueCapability(string v) { - return config.CheckValueCapability(v); - } - StartupScreenGenericConfig@ GetConfig() { - return config; - } - private void StateChanged(spades::ui::UIElement@) { - config.SetValue(button.Toggled ? valueOn : valueOff); - } - void SetHelpTextHandler(HelpTextHandler@ handler) { - HelpHandler(handler, desc).Watch(button); - } - - void set_Bounds(AABB2 v) { - UIElement::set_Bounds(v); - Vector2 size = this.Size; - float h = 24.f; - button.Bounds = AABB2(0.f, 0.f, size.x, h); - } - } - - class StartupScreenConfigSliderItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor, LabelAddable { - - private StartupScreenUI@ ui; - private string desc; - private double stepSize; - private StartupScreenGenericConfig@ config; - private spades::ui::Slider@ slider; - private spades::ui::Label@ valueLabel; - private ConfigNumberFormatter@ formatter; - private string label; - - StartupScreenConfigSliderItemEditor(StartupScreenUI@ ui, - StartupScreenGenericConfig@ cfg, - double minValue, double maxValue, double stepSize, string label, string desc, - ConfigNumberFormatter@ formatter) { - super(ui.manager); - @this.ui = ui; - @config = cfg; - this.desc = desc; - this.stepSize = stepSize; - this.label = label; - - AddLabel(0, 0, 24.f, label); - - spades::ui::Slider b(Manager); - b.MinValue = minValue; - b.MaxValue = maxValue; - - // compute large change - int steps = int((maxValue - minValue) / stepSize); - steps = (steps + 9) / 10; - b.LargeChange = float(steps) * stepSize; - b.SmallChange = stepSize; - - @b.Changed = spades::ui::EventHandler(this.StateChanged); - @slider = b; - this.AddChild(b); - - spades::ui::Label l(Manager); - @valueLabel = l; - l.Alignment = Vector2(1.f, 0.5f); - this.AddChild(l); - - @this.formatter = formatter; - } - - private void UpdateLabel() { - double v = slider.Value; - string s = formatter.Format(v); - valueLabel.Text = s; - } - - string GetLabel() { - return label; - } - - void LoadConfig() { - string val = config.GetValue(); - double v = ParseDouble(val); - // don't use `ScrollTo` here; this calls `Changed` so - // out-of-range value will be clamped and saved back - slider.Value = Clamp(v, slider.MinValue, slider.MaxValue); - // FIXME: button.Enable = CheckValueCapability(valueOn).length == 0; - UpdateLabel(); - } - string CheckValueCapability(string v) { - return ""; //FIXME: config.CheckValueCapability(v); - } - StartupScreenGenericConfig@ GetConfig() { - return config; - } - - void DoRounding() { - double v = double(slider.Value - slider.MinValue); - v = floor((v / stepSize) + 0.5) * stepSize; - v += double(slider.MinValue); - slider.Value = v; - } - private void StateChanged(spades::ui::UIElement@) { - DoRounding(); - config.SetValue(ToString(slider.Value)); - UpdateLabel(); - } - void SetHelpTextHandler(HelpTextHandler@ handler) { - HelpHandler(handler, desc).WatchDeep(slider); - } - - void set_Bounds(AABB2 v) { - UIElement::set_Bounds(v); - Vector2 size = this.Size; - float h = 24.f; - slider.Bounds = AABB2(100.f, 2.f, size.x - 180.f, h - 4.f); - valueLabel.Bounds = AABB2(size.x - 80.f, 0.f, 80.f, h); - } - } - - - class StartupScreenConfigFieldItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor, LabelAddable { - - private StartupScreenUI@ ui; - private string desc; - private double stepSize; - private StartupScreenGenericConfig@ config; - private spades::ui::Field@ field; - private string label; - - StartupScreenConfigFieldItemEditor(StartupScreenUI@ ui, - StartupScreenGenericConfig@ cfg, - string label, string desc) { - super(ui.manager); - @this.ui = ui; - @config = cfg; - this.desc = desc; - this.label = label; - - AddLabel(0, 0, 24.f, label); - - spades::ui::Field b(Manager); - - @b.Changed = spades::ui::EventHandler(this.StateChanged); - @field = b; - this.AddChild(b); - - } - - string GetLabel() { - return label; - } - - void LoadConfig() { - string val = config.GetValue(); - field.Text = val; - } - string CheckValueCapability(string v) { - return config.CheckValueCapability(v); - } - StartupScreenGenericConfig@ GetConfig() { - return config; - } - - private void StateChanged(spades::ui::UIElement@) { - config.SetValue(field.Text); - } - void SetHelpTextHandler(HelpTextHandler@ handler) { - HelpHandler(handler, desc).WatchDeep(field); - } - - void set_Bounds(AABB2 v) { - UIElement::set_Bounds(v); - Vector2 size = this.Size; - float h = 24.f; - field.Bounds = AABB2(240.f, 0.f, size.x - 240.f, h); - } - } - - // Maps multiple configs to {"0", "1", ...} or "custom" - class StartupScreenComplexConfig: StartupScreenGenericConfig { - StartupScreenConfigItemEditor@[] editors; - StartupScreenComplexConfigPreset@[] presets; - - void AddEditor(StartupScreenConfigItemEditor@ editor) { - Assert(presets.length == 0); - editors.insertLast(editor); - } - void AddPreset(StartupScreenComplexConfigPreset@ preset) { - Assert(preset.Values.length == editors.length); - presets.insertLast(preset); - } - - string GetValue() { - string[] values; - - for(uint i = 0; i < editors.length; i++) { - values.insertLast(editors[i].GetConfig().GetValue()); - } - for(uint i = 0; i < presets.length; i++) { - string[]@ pval = presets[i].Values; - uint j = 0; - uint jc = pval.length; - for(; j < jc; j++) { - if(values[j] != pval[j]) { - break; - } - } - if(j == jc) { - return ToString(int(i)); - } - } - return "custom"; - } - void SetValue(string v) { - if(v == "custom") { - return; - } - uint pId = uint(ParseInt(v)); - string[]@ pval = presets[pId].Values; - for(uint i = 0; i < pval.length; i++) { - editors[i].GetConfig().SetValue(pval[i]); - } - } - string CheckValueCapability(string v) { - if(v == "custom") { - return ""; - } - uint pId = uint(ParseInt(v)); - string[]@ pval = presets[pId].Values; - string ret = ""; - for(uint i = 0; i < pval.length; i++) { - ret += editors[i].GetConfig().CheckValueCapability(pval[i]); - } - return ret; - } - - // used for StartupScreenConfigSelectItemEditor's ctor param - string GetValuesString() { - string[] lst; - for(uint i = 0; i < presets.length; i++) { - lst.insertLast(ToString(int(i))); - } - lst.insertLast("custom"); - return join(lst, "|"); - } - // used for StartupScreenConfigSelectItemEditor's ctor param - string GetDescriptionsString() { - string[] lst; - for(uint i = 0; i < presets.length; i++) { - lst.insertLast(presets[i].Name); - } - lst.insertLast(_Tr("StartupScreen", "Custom")); - return join(lst, "|"); - } - } - - class StartupScreenComplexConfigPreset { - string Name; - string[] Values; - StartupScreenComplexConfigPreset(string name, string values) { - Name = name; - Values = values.split("|"); - } - } - - class StartupScreenConfigComplexItemEditor: StartupScreenConfigSelectItemEditor { - private string dlgTitle; - - StartupScreenConfigComplexItemEditor(StartupScreenUI@ ui, - StartupScreenComplexConfig@ cfg, - string label, string desc) { - super(ui, cfg, cfg.GetValuesString(), - label + ":" + desc + "|" + cfg.GetDescriptionsString()); - dlgTitle = label; - @buttons[buttons.length - 1].Activated = spades::ui::EventHandler(this.CustomClicked); - } - - private void CustomClicked(spades::ui::UIElement@) { - RunDialog(); - } - - private void DialogDone(spades::ui::UIElement@) { - LoadConfig(); - } - - private void RunDialog() { - StartupScreenComplexConfigDialog dlg(ui.manager, cast(config)); - @dlg.DialogDone = spades::ui::EventHandler(this.DialogDone); - dlg.Title = dlgTitle; - dlg.RunDialog(); - } - } - - class StartupScreenConfigView: spades::ui::ListViewBase { - private StartupScreenConfigViewModel vmodel; - - StartupScreenConfigView(spades::ui::UIManager@ manager) { - super(manager); - this.RowHeight = 30.f; - } - void Finalize() { - @this.Model = vmodel; - } - void AddRow(spades::ui::UIElement@ elm) { - vmodel.items.insertLast(elm); - } - - void Filter(string text) { - vmodel.Filter(text); - Reload(); - } - - void SetHelpTextHandler(HelpTextHandler@ handler) { - spades::ui::UIElement@[]@ elms = vmodel.items; - for(uint i = 0; i < elms.length; i++) { - StartupScreenConfigItemEditor@ item = cast(elms[i]); - if(item !is null) { - item.SetHelpTextHandler(handler); - } - } - } - void LoadConfig() { - spades::ui::UIElement@[]@ elms = vmodel.items; - for(uint i = 0; i < elms.length; i++) { - StartupScreenConfigItemEditor@ item = cast(elms[i]); - if(item !is null) { - item.LoadConfig(); - } - } - } - } - - class StartupScreenComplexConfigDialog: spades::ui::UIElement { - StartupScreenComplexConfig@ config; - float ContentsTop = 50.f; - float ContentsHeight; - string Title; - - spades::ui::TextViewer@ helpView; - StartupScreenConfigView@ configView; - - spades::ui::EventHandler@ DialogDone; - - private spades::ui::UIElement@ oldRoot; - - StartupScreenComplexConfigDialog(spades::ui::UIManager@ manager, - StartupScreenComplexConfig@ config) { - super(manager); - @this.config = config; - Vector2 size = manager.RootElement.Size; - ContentsHeight = size.y - ContentsTop * 2.f; - - float mainWidth = size.x - 250.f; - { - spades::ui::TextViewer e(Manager); - e.Bounds = AABB2(mainWidth, ContentsTop + 30.f, size.x - mainWidth - 10.f, ContentsHeight - 60.f); - AddChild(e); - @helpView = e; - } - - { - StartupScreenConfigView cfg(Manager); - - StartupScreenConfigItemEditor@[]@ editors = config.editors; - - for(uint i = 0; i < editors.length; i++) - cfg.AddRow(cast(editors[i])); - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(10.f, ContentsTop + 30.f, mainWidth - 20.f, ContentsHeight - 60.f); - AddChild(cfg); - @configView = cfg; - } - - { - spades::ui::Button button(Manager); - button.Caption = _Tr("StartupScreen", "Close"); - button.Bounds = AABB2(size.x - 160.f, ContentsTop + ContentsHeight - 30.f, 150.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.CloseActivated); - AddChild(button); - } - - } - - private void CloseActivated(spades::ui::UIElement@) { - if(oldRoot !is null) { - oldRoot.Enable = true; - @oldRoot = null; - } - @this.Parent = null; - DialogDone(this); - } - - private void HandleHelpText(string s) { - helpView.Text = s; - } - - void RunDialog() { - spades::ui::UIElement@ root = Manager.RootElement; - - spades::ui::UIElementIterator iterator(root); - while(iterator.MoveNext()) { - spades::ui::UIElement@ e = iterator.Current; - if(e.Enable and e.Visible) { - @oldRoot = e; - e.Enable = false; - break; - } - } - - this.Bounds = root.Bounds; - root.AddChild(this); - - configView.LoadConfig(); - } - - void Render() { - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Renderer@ r = Manager.Renderer; - Image@ img = r.RegisterImage("Gfx/White.tga"); - - r.ColorNP = Vector4(0, 0, 0, 0.8f); - r.DrawImage(img, - AABB2(pos.x, pos.y, size.x, ContentsTop - 15.f)); - r.DrawImage(img, - AABB2(pos.x, ContentsTop + ContentsHeight + 15.f, size.x, ContentsTop - 15.f)); - - r.ColorNP = Vector4(0, 0, 0, 0.95f); - r.DrawImage(img, - AABB2(pos.x, ContentsTop - 15.f, size.x, ContentsHeight + 30.f)); - - r.ColorNP = Vector4(1, 1, 1, 0.08f); - r.DrawImage(img, - AABB2(pos.x, pos.y + ContentsTop - 15.f, size.x, 1.f)); - r.DrawImage(img, - AABB2(pos.x, pos.y + ContentsTop + ContentsHeight + 15.f, size.x, 1.f)); - r.ColorNP = Vector4(1, 1, 1, 0.2f); - r.DrawImage(img, - AABB2(pos.x, pos.y + ContentsTop - 14.f, size.x, 1.f)); - r.DrawImage(img, - AABB2(pos.x, pos.y + ContentsTop + ContentsHeight + 14.f, size.x, 1.f)); - - Font@ font = Font; - r.ColorNP = Vector4(0.8f, 0.8f, 0.8f, 1.f); - r.DrawImage(img, - AABB2(pos.x, pos.y + ContentsTop, size.x, 20.f)); - font.Draw(Title, Vector2(pos.x + 10.f, pos.y + ContentsTop), 1.f, Vector4(0.f, 0.f, 0.f, 1.f)); - - UIElement::Render(); - } - - } - - class StartupScreenGraphicsTab: spades::ui::UIElement, LabelAddable { - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - StartupScreenGraphicsDisplayResolutionEditor@ resEdit; - - spades::ui::CheckBox@ fullscreenCheck; - spades::ui::RadioButton@ driverOpenGL; - spades::ui::RadioButton@ driverSoftware; - - spades::ui::TextViewer@ helpView; - StartupScreenConfigView@ configViewGL; - StartupScreenConfigView@ configViewSoftware; - - private ConfigItem r_renderer("r_renderer"); - private ConfigItem r_fullscreen("r_fullscreen"); - - StartupScreenGraphicsTab(StartupScreenUI@ ui, Vector2 size) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - - float mainWidth = size.x - 250.f; - - { - spades::ui::TextViewer e(Manager); - e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); - @e.Font = ui.fontManager.GuiFont; - e.Text = _Tr("StartupScreen", "Graphics Settings"); - AddChild(e); - @helpView = e; - } - - AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", "Resolution")); - { - StartupScreenGraphicsDisplayResolutionEditor e(ui); - e.Bounds = AABB2(100.f, 0.f, 124.f, 24.f); - AddChild(e); - @resEdit = e; - } - - { - spades::ui::CheckBox e(Manager); - e.Caption = _Tr("StartupScreen", "Fullscreen Mode"); - e.Bounds = AABB2(230.f, 0.f, 200.f, 24.f); - HelpHandler(helpView, - _Tr("StartupScreen", "By running in fullscreen mode OpenSpades occupies the " - "screen, making it easier for you to concentrate on playing the game.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnFullscreenCheck); - AddChild(e); - @fullscreenCheck = e; - } - - AddLabel(0.f, 30.f, 24.f, _Tr("StartupScreen", "Backend")); - { - spades::ui::RadioButton e(Manager); - e.Caption = _Tr("StartupScreen", "OpenGL"); - e.Bounds = AABB2(100.f, 30.f, 140.f, 24.f); - e.GroupName = "driver"; - HelpHandler(helpView, - _Tr("StartupScreen", "OpenGL renderer uses your computer's graphics " - "accelerator to generate the game screen.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnDriverOpenGL); - AddChild(e); - @driverOpenGL = e; - } - { - spades::ui::RadioButton e(Manager); - e.Caption = _Tr("StartupScreen", "Software"); - e.Bounds = AABB2(250.f, 30.f, 140.f, 24.f); - e.GroupName = "driver"; - HelpHandler(helpView, - _Tr("StartupScreen", "Software renderer uses CPU to generate the game " - "screen. Its quality and performance might be inferior to OpenGL " - "renderer, but it works even with an unsupported GPU.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnDriverSoftware); - AddChild(e); - @driverSoftware = e; - } - - { - StartupScreenConfigView cfg(Manager); - - cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, - StartupScreenGraphicsAntialiasConfig(ui), "0|2|4|fxaa", - _Tr("StartupScreen", - "Antialias:Enables a technique to improve the appearance of high-contrast edges.\n\n" - "MSAA: Performs antialiasing by generating an intermediate high-resolution image. " - "Looks best, but doesn't cope with some settings.\n\n" - "FXAA: Performs antialiasing by smoothing artifacts out as a post-process.|" - "Off|MSAA 2x|4x|FXAA"))); - - cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_radiosity"), "0", "1", _Tr("StartupScreen", "Global Illumination"), - _Tr("StartupScreen", - "Enables a physically based simulation of light path for more realistic lighting."))); - - cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_hdr"), "0", "1", _Tr("StartupScreen", "Linear HDR Rendering"), - _Tr("StartupScreen", - "Uses a number representation which allows wider dynamic range during rendering process. " - "Additionally, this allows color calculation whose value is in linear correspondence with actual energy, " - "that is, physically accurate blending can be achieved."))); - - { - StartupScreenComplexConfig cplx; - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_cameraBlur"), "0", "1", _Tr("StartupScreen", "Camera Blur"), - _Tr("StartupScreen", "Blurs the screen when you turn quickly."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_lens"), "0", "1", _Tr("StartupScreen", "Lens Effect"), - _Tr("StartupScreen", "Simulates distortion caused by a real camera lens."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_bloom"), "0", "1", _Tr("StartupScreen", "Lens Scattering Filter"), - _Tr("StartupScreen", "Simulates light being scattered by dust on the camera lens."))); - // r_lens is currently no-op - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_lensFlare"), "0", "1", _Tr("StartupScreen", "Lens Flare"), - _Tr("StartupScreen", "The Sun causes lens flare."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_lensFlareDynamic"), "0", "1", _Tr("StartupScreen", "Flares for Dynamic Lights"), - _Tr("StartupScreen", "Enables lens flare for light sources other than the Sun."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_colorCorrection"), "0", "1", _Tr("StartupScreen", "Color Correction"), - _Tr("StartupScreen", "Applies cinematic color correction to make the image look better."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_depthOfField"), "0", "1", _Tr("StartupScreen", "Depth of Field"), - _Tr("StartupScreen", "Blurs out-of-focus objects."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_ssao"), "0", "1", _Tr("StartupScreen", "Screen Space Ambient Occlusion"), - _Tr("StartupScreen", "Simulates soft shadows that occur between nearby objects."))); - - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "0|0|0|0|0|0|0|0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Medium"), "1|0|0|1|0|1|0|0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "1|1|1|1|1|1|1|0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Ultra"), "1|1|1|1|1|1|1|1")); - - cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, - _Tr("StartupScreen", "Post-process"), - _Tr("StartupScreen", "Post-process modifies the image to make it look better and " - "more realistic."))); - } - - cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, - StartupScreenConfig(ui, "r_softParticles"), "0|1|2", - _Tr("StartupScreen", - "Particles|" - "Low:Artifact occurs when a particle intersects other objects.|" - "Medium:Particle intersects objects smoothly.|" - "High:Particle intersects objects smoothly, and some objects casts " - "their shadow to particles."))); - - { - StartupScreenComplexConfig cplx; - // r_mapSoftShadow is currently no-op - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_dlights"), "0", "1", _Tr("StartupScreen", "Dynamic Lights"), - _Tr("StartupScreen", - "Gives some objects an ability to emit light to give them " - "an energy-emitting impression."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_modelShadows"), "0", "1", _Tr("StartupScreen", "Shadows"), - _Tr("StartupScreen", "Non-static object casts a shadow."))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_fogShadow"), "0", "1", _Tr("StartupScreen", "Volumetric Fog"), - _Tr("StartupScreen", "Simulates shadow being casted to the fog particles using a " - "super highly computationally demanding algorithm. "))); - cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "r_physicalLighting"), "0", "1", _Tr("StartupScreen", "Physically Based Lighting"), - _Tr("StartupScreen", "Uses more accurate approximation techniques to decide the brightness of objects."))); - - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "1|0|0|0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Medium"), "1|1|0|0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "1|1|0|1")); - - cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, - _Tr("StartupScreen", "Direct Lights"), - _Tr("StartupScreen", "Controls how light encounting a material and atmosphere directly " - "affects its appearance."))); - } - - { - StartupScreenComplexConfig cplx; - - cplx.AddEditor(StartupScreenConfigSelectItemEditor(ui, - StartupScreenConfig(ui, "r_water"), "0|1|2|3", - _Tr("StartupScreen", - "Water Shader|" - "None:Water is rendered in the same way that normal blocks are done.|" - "Level 1:Refraction and the reflected Sun are simulated.|" - "Level 2:Waving water is simulated as well as reflection and refraction.|" - "Level 3:Reflections and refractions are rendered at the highest quality using screen-space techniques."))); - - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "0")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Med"), "1")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "2")); - cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Ultra"), "3")); - - cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, - _Tr("StartupScreen", "Shader Effects"), _Tr("StartupScreen", "Special effects."))); - } - - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(0.f, 60.f, mainWidth, size.y - 60.f); - AddChild(cfg); - @configViewGL = cfg; - } - - { - StartupScreenConfigView cfg(Manager); - - cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, - StartupScreenConfig(ui, "r_swUndersampling"), "0|1|2", - _Tr("StartupScreen", - "Fast Mode:Reduces the image resolution to make the rendering faster.|" - "Off|2x|4x"))); - - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(0.f, 60.f, mainWidth, size.y - 60.f); - AddChild(cfg); - @configViewSoftware = cfg; - } - - } - - private void HandleHelpText(string text) { - helpView.Text = text; - } - - private void OnDriverOpenGL(spades::ui::UIElement@){ r_renderer.StringValue = "gl"; LoadConfig(); } - private void OnDriverSoftware(spades::ui::UIElement@){ r_renderer.StringValue = "sw"; LoadConfig(); } - - private void OnFullscreenCheck(spades::ui::UIElement@) - { r_fullscreen.IntValue = fullscreenCheck.Toggled ? 1 : 0; } - - void LoadConfig() { - resEdit.LoadConfig(); - if(r_renderer.StringValue == "sw") { - driverSoftware.Check(); - configViewGL.Visible = false; - configViewSoftware.Visible = true; - }else{ - driverOpenGL.Check(); - configViewGL.Visible = true; - configViewSoftware.Visible = false; - } - fullscreenCheck.Toggled = r_fullscreen.IntValue != 0; - driverOpenGL.Enable = ui.helper.CheckConfigCapability("r_renderer", "gl").length == 0; - driverSoftware.Enable = ui.helper.CheckConfigCapability("r_renderer", "sw").length == 0; - configViewGL.LoadConfig(); - configViewSoftware.LoadConfig(); - - } - - - - } - - class StartupScreenComboBoxDropdownButton: spades::ui::SimpleButton { - StartupScreenComboBoxDropdownButton(spades::ui::UIManager@ manager) { - super(manager); - } - void Render() { - SimpleButton::Render(); - - Renderer@ renderer = Manager.Renderer; - Image@ image = renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); - AABB2 bnd = ScreenBounds; - Vector2 p = (bnd.min + bnd.max) * 0.5f + Vector2(-8.f, 8.f); - renderer.DrawImage(image, AABB2(p.x, p.y, 16.f, -16.f)); - } - } - - class StartupScreenGraphicsDisplayResolutionEditor: spades::ui::UIElement { - spades::ui::Field@ widthField; - spades::ui::Field@ heightField; - spades::ui::Button@ dropdownButton; - private ConfigItem r_videoWidth("r_videoWidth"); - private ConfigItem r_videoHeight("r_videoHeight"); - StartupScreenHelper@ helper; - - StartupScreenGraphicsDisplayResolutionEditor(StartupScreenUI@ ui) { - super(ui.manager); - @helper = ui.helper; - { - spades::ui::Field e(Manager); - AddChild(e); - e.Bounds = AABB2(0, 0, 45.f, 24.f); - e.DenyNonAscii = true; - @e.Changed = spades::ui::EventHandler(this.ValueEditHandler); - @widthField = e; - } - { - spades::ui::Field e(Manager); - AddChild(e); - e.Bounds = AABB2(53, 0, 45.f, 24.f); - e.DenyNonAscii = true; - @e.Changed = spades::ui::EventHandler(this.ValueEditHandler); - @heightField = e; - } - { - StartupScreenComboBoxDropdownButton e(Manager); - AddChild(e); - e.Bounds = AABB2(100, 0, 24.f, 24.f); - @e.Activated = spades::ui::EventHandler(this.ShowDropdown); - @dropdownButton = e; - } - } - - void LoadConfig() { - widthField.Text = ToString(r_videoWidth.IntValue); - heightField.Text = ToString(r_videoHeight.IntValue); - } - - void SaveConfig() { - int w = ParseInt(widthField.Text); - int h = ParseInt(heightField.Text); - if(w < 640 or h < 480 or w > 8192 or h > 8192) return; - r_videoWidth.IntValue = w; - r_videoHeight.IntValue = h; - } - - private void ValueEditHandler(spades::ui::UIElement@) { - SaveConfig(); - } - - private void DropdownHandler(int index) { - if(index >= 0) { - widthField.Text = ToString(helper.GetVideoModeWidth(index)); - heightField.Text = ToString(helper.GetVideoModeHeight(index)); - - SaveConfig(); - } - } - - private void ShowDropdown(spades::ui::UIElement@) { - string[] items = {}; - int cnt = helper.GetNumVideoModes(); - for(int i = 0; i < cnt; i++) { - int w = helper.GetVideoModeWidth(i); - int h = helper.GetVideoModeHeight(i); - string s = ToString(w) + "x" + ToString(h); - items.insertLast(s); - } - spades::ui::ShowDropDownList(this, items, spades::ui::DropDownListHandler(this.DropdownHandler)); - } - void Render() { - Font@ font = this.Font; - font.Draw("x", Vector2(45.f, 0.f) + ScreenPosition, 1.f, Vector4(1.f, 1.f, 1.f, 1.f)); - UIElement::Render(); - } - } - - class StartupScreenGraphicsAntialiasConfig: StartupScreenGenericConfig { - private StartupScreenUI@ ui; - private ConfigItem@ msaaConfig; - private ConfigItem@ fxaaConfig; - StartupScreenGraphicsAntialiasConfig(StartupScreenUI@ ui) { - @this.ui = ui; - @msaaConfig = ConfigItem("r_multisamples"); - @fxaaConfig = ConfigItem("r_fxaa"); - } - string GetValue() { - if(fxaaConfig.IntValue != 0) { - return "fxaa"; - }else{ - int v = msaaConfig.IntValue; - if(v < 2) return "0"; - else return msaaConfig.StringValue; - } - } - void SetValue(string v) { - if(v == "fxaa") { - msaaConfig.StringValue = "0"; - fxaaConfig.StringValue = "1"; - } else if (v == "0" || v == "1") { - msaaConfig.StringValue = "0"; - fxaaConfig.StringValue = "0"; - } else { - msaaConfig.StringValue = v; - fxaaConfig.StringValue = "0"; - } - } - string CheckValueCapability(string v) { - if(v == "fxaa") { - return ui.helper.CheckConfigCapability("r_multisamples", "0") + - ui.helper.CheckConfigCapability("r_fxaa", "1"); - } else if (v == "0" || v == "1") { - return ui.helper.CheckConfigCapability("r_multisamples", "0") + - ui.helper.CheckConfigCapability("r_fxaa", "0"); - } else { - return ui.helper.CheckConfigCapability("r_multisamples", v) + - ui.helper.CheckConfigCapability("r_fxaa", "0"); - } - - } - } - - - class StartupScreenAudioTab: spades::ui::UIElement, LabelAddable { - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - spades::ui::RadioButton@ driverOpenAL; - spades::ui::RadioButton@ driverYSR; - spades::ui::RadioButton@ driverNull; - - spades::ui::TextViewer@ helpView; - StartupScreenConfigView@ configViewOpenAL; - StartupScreenConfigView@ configViewYSR; - - private ConfigItem s_audioDriver("s_audioDriver"); - private ConfigItem s_eax("s_eax"); - - StartupScreenAudioTab(StartupScreenUI@ ui, Vector2 size) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - - float mainWidth = size.x - 250.f; - - { - spades::ui::TextViewer e(Manager); - e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); - @e.Font = ui.fontManager.GuiFont; - e.Text = _Tr("StartupScreen", "Audio Settings"); - AddChild(e); - @helpView = e; - } - - - AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", "Backend")); - { - spades::ui::RadioButton e(Manager); - e.Caption = _Tr("StartupScreen", "OpenAL"); - e.Bounds = AABB2(100.f, 0.f, 100.f, 24.f); - e.GroupName = "driver"; - HelpHandler(helpView, - _Tr("StartupScreen", "Uses an OpenAL-capable sound card to process sound. " - "In most cases where there isn't such a sound card, software emulation is " - "used.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnDriverOpenAL); - AddChild(e); - @driverOpenAL = e; - } - { - spades::ui::RadioButton e(Manager); - e.Caption = _Tr("StartupScreen", "YSR"); - e.Bounds = AABB2(210.f, 0.f, 100.f, 24.f); - e.GroupName = "driver"; - HelpHandler(helpView, - _Tr("StartupScreen", "YSR is an experimental 3D HDR sound engine optimized " - "for OpenSpades. It features several enhanced features including " - "automatic load control, dynamics compressor, HRTF-based " - "3D audio, and high quality reverb.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnDriverYSR); - AddChild(e); - @driverYSR = e; - } - { - spades::ui::RadioButton e(Manager); - //! The name of audio driver that outputs no audio. - e.Caption = _Tr("StartupScreen", "Null"); - e.Bounds = AABB2(320.f, 0.f, 100.f, 24.f); - e.GroupName = "driver"; - HelpHandler(helpView, - _Tr("StartupScreen", "Disables audio output.")).Watch(e); - @e.Activated = spades::ui::EventHandler(this.OnDriverNull); - AddChild(e); - @driverNull = e; - } - - { - StartupScreenConfigView cfg(Manager); - - cfg.AddRow(StartupScreenConfigSliderItemEditor(ui, - StartupScreenConfig(ui, "s_maxPolyphonics"), 16.0, 256.0, 8.0, - _Tr("StartupScreen", "Polyphonics"), _Tr("StartupScreen", - "Specifies how many sounds can be played simultaneously. " - "Higher value needs more processing power, so setting this too high might " - "cause an overload (especially with a software emulation)."), - ConfigNumberFormatter(0, " poly"))); - - cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, - StartupScreenConfig(ui, "s_eax"), "0", "1", - _Tr("StartupScreen", "EAX"), _Tr("StartupScreen", - "Enables extended features provided by the OpenAL driver to create " - "more ambience."))); - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(0.f, 30.f, mainWidth, size.y - 30.f); - AddChild(cfg); - @configViewOpenAL = cfg; - } - - { - StartupScreenConfigView cfg(Manager); - - cfg.AddRow(StartupScreenConfigSliderItemEditor(ui, - StartupScreenConfig(ui, "s_maxPolyphonics"), 16.0, 256.0, 8.0, - _Tr("StartupScreen", "Polyphonics"), _Tr("StartupScreen", - "Specifies how many sounds can be played simultaneously. " - "No matter what value is set, YSR might reduce the number of sounds " - "when an overload is detected."), - ConfigNumberFormatter(0, " poly"))); - - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(0.f, 30.f, mainWidth, size.y - 30.f); - AddChild(cfg); - @configViewYSR = cfg; - } - - } - - private void HandleHelpText(string text) { - helpView.Text = text; - } - - private void OnDriverOpenAL(spades::ui::UIElement@){ s_audioDriver.StringValue = "openal"; LoadConfig(); } - private void OnDriverYSR(spades::ui::UIElement@){ s_audioDriver.StringValue = "ysr"; LoadConfig(); } - private void OnDriverNull(spades::ui::UIElement@){ s_audioDriver.StringValue = "null"; LoadConfig(); } - - void LoadConfig() { - if(s_audioDriver.StringValue == "ysr") { - driverYSR.Check(); - configViewOpenAL.Visible = false; - configViewYSR.Visible = true; - }else if(s_audioDriver.StringValue == "openal"){ - driverOpenAL.Check(); - configViewOpenAL.Visible = true; - configViewYSR.Visible = false; - }else if(s_audioDriver.StringValue == "null"){ - driverNull.Check(); - configViewOpenAL.Visible = false; - configViewYSR.Visible = false; - } - driverOpenAL.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "openal").length == 0; - driverYSR.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "ysr").length == 0; - driverNull.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "null").length == 0; - configViewOpenAL.LoadConfig(); - configViewYSR.LoadConfig(); - - } - - - - } - - class StartupScreenGenericTab: spades::ui::UIElement, LabelAddable { - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - StartupScreenLocaleEditor@ locale; - - StartupScreenGenericTab(StartupScreenUI@ ui, Vector2 size) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - - string label = _Tr("StartupScreen", "Language"); - if (label != "Language") { - label += " (Language)"; - } - AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", label)); - { - StartupScreenLocaleEditor e(ui); - AddChild(e); - e.Bounds = AABB2(160.f, 0.f, 400.f, 24.f); - @locale = e; - } - - AddLabel(0.f, 30.f, 30.f, _Tr("StartupScreen", "Tools")); - { - spades::ui::Button button(Manager); - button.Caption = _Tr("StartupScreen", "Reset All Settings"); - button.Bounds = AABB2(160.f, 30.f, 350.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnResetSettingsPressed); - AddChild(button); - } - { - spades::ui::Button button(Manager); - string osType = helper.OperatingSystemType; - if (osType == "Windows") { - button.Caption = _Tr("StartupScreen", "Open Config Folder in Explorer"); - } else if (osType == "Mac") { - button.Caption = _Tr("StartupScreen", "Reveal Config Folder in Finder"); - } else { - button.Caption = _Tr("StartupScreen", "Browse Config Folder"); - } - button.Bounds = AABB2(160.f, 66.f, 350.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnBrowseUserDirectoryPressed); - AddChild(button); - } - } - - void LoadConfig() { - locale.LoadConfig(); - } - - private void OnBrowseUserDirectoryPressed(spades::ui::UIElement@) { - if (helper.BrowseUserDirectory()) { - return; - } - - string msg = _Tr("StartupScreen", "An unknown error has occurred while opening the config directory."); - AlertScreen al(Parent, msg, 100.f); - al.Run(); - } - - private void OnResetSettingsPressed(spades::ui::UIElement@) { - string msg = _Tr("StartupScreen", "Are you sure to reset all settings? They include (but are not limited to):") + "\n" + - "- " + _Tr("StartupScreen", "All graphics/audio settings") + "\n" + - "- " + _Tr("StartupScreen", "All key bindings") + "\n" + - "- " + _Tr("StartupScreen", "Your player name") + "\n" + - "- " + _Tr("StartupScreen", "Other advanced settings only accessible through '{0}' tab and in-game commands", - _Tr("StartupScreen", "Advanced")); - ConfirmScreen al(Parent, msg, 200.f); - @al.Closed = spades::ui::EventHandler(OnResetSettingsConfirmed); - al.Run(); - } - - private void OnResetSettingsConfirmed(spades::ui::UIElement@ sender) { - if (!cast(sender).Result) { - return; - } - - ResetAllSettings(); - - // Reload the startup screen so the language config takes effect - ui.Reload(); - } - - private void ResetAllSettings() { - string[]@ names = GetAllConfigNames(); - - for(uint i = 0, count = names.length; i < count; i++) { - ConfigItem item(names[i]); - item.StringValue = item.DefaultValue; - } - - // Some of default values may be infeasible for the user's system. - helper.FixConfigs(); - } - - } - - class StartupScreenDropdownListDropdownButton: spades::ui::SimpleButton { - StartupScreenDropdownListDropdownButton(spades::ui::UIManager@ manager) { - super(manager); - Alignment = Vector2(0.f, 0.5f); - } - void Render() { - SimpleButton::Render(); - - Renderer@ renderer = Manager.Renderer; - Image@ arrowImg = renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); - - AABB2 bnd = ScreenBounds; - Vector2 p = (bnd.min + bnd.max) * 0.5f + Vector2(-8.f, 8.f); - renderer.DrawImage(arrowImg, AABB2(bnd.max.x - 16.f, p.y, 16.f, -16.f)); - } - } - - class StartupScreenLocaleEditor: spades::ui::UIElement, LabelAddable {StartupScreenUI@ ui; - StartupScreenHelper@ helper; - private ConfigItem core_locale("core_locale"); - - spades::ui::Button@ dropdownButton; - - StartupScreenLocaleEditor(StartupScreenUI@ ui) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - { - StartupScreenDropdownListDropdownButton e(Manager); - AddChild(e); - e.Bounds = AABB2(0.f, 0.f, 400.f, 24.f); - @e.Activated = spades::ui::EventHandler(this.ShowDropdown); - @dropdownButton = e; - } - } - - void LoadConfig() { - string locale = core_locale.StringValue; - string name = _Tr("StartupScreen", "Unknown ({0})", locale); - if (locale == "") { - name = _Tr("StartupScreen", "System default"); - } - - int cnt = helper.GetNumLocales(); - for(int i = 0; i < cnt; i++) { - if (locale == helper.GetLocale(i)) { - name = helper.GetLocaleDescriptionNative(i) + " / " + helper.GetLocaleDescriptionEnglish(i); - } - } - - dropdownButton.Caption = name; - } - - private void ShowDropdown(spades::ui::UIElement@) { - string[] items = {_Tr("StartupScreen", "System default")}; - int cnt = helper.GetNumLocales(); - for(int i = 0; i < cnt; i++) { - string s = helper.GetLocaleDescriptionNative(i) + " / " + helper.GetLocaleDescriptionEnglish(i); - items.insertLast(s); - } - spades::ui::ShowDropDownList(this, items, spades::ui::DropDownListHandler(this.DropdownHandler)); - } - - private void DropdownHandler(int index) { - if(index >= 0) { - if (index == 0) { - core_locale = ""; - } else { - core_locale = helper.GetLocale(index - 1); - } - - // Reload the startup screen so the language config takes effect - ui.Reload(); - } - } - } - - class StartupScreenSystemInfoTab: spades::ui::UIElement, LabelAddable { - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - spades::ui::TextViewer@ helpView; - - StartupScreenSystemInfoTab(StartupScreenUI@ ui, Vector2 size) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - - { - spades::ui::TextViewer e(Manager); - e.Bounds = AABB2(0.f, 0.f, size.x, size.y - 30.f); - @e.Font = ui.fontManager.GuiFont; - AddChild(e); - for(int i = 0, count = helper.GetNumReportLines(); i < count; i++) { - string text = helper.GetReportLineText(i); - Vector4 col = helper.GetReportLineColor(i); - e.AddLine(text, true, col); - } - @helpView = e; - } - - { - spades::ui::Button button(Manager); - button.Caption = _Tr("StartupScreen", "Copy to Clipboard"); - button.Bounds = AABB2(size.x - 180.f, size.y - 30.f, 180.f, 30.f); - @button.Activated = spades::ui::EventHandler(this.OnCopyReport); - AddChild(button); - } - - } - private void OnCopyReport(spades::ui::UIElement@){ - Manager.Copy(helper.GetReport()); - } - - - - } - - - class StartupScreenAdvancedTab: spades::ui::UIElement, LabelAddable { - StartupScreenUI@ ui; - StartupScreenHelper@ helper; - - spades::ui::Field@ filter; - - spades::ui::TextViewer@ helpView; - StartupScreenConfigView@ configView; - - StartupScreenAdvancedTab(StartupScreenUI@ ui, Vector2 size) { - super(ui.manager); - @this.ui = ui; - @helper = ui.helper; - - float mainWidth = size.x - 250.f; - - { - spades::ui::TextViewer e(Manager); - e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); - @e.Font = ui.fontManager.GuiFont; - e.Text = _Tr("StartupScreen", "Advanced Settings"); - e.Visible = false; - AddChild(e); - @helpView = e; - } - - - { - spades::ui::Field e(Manager); - e.Placeholder = _Tr("StartupScreen", "Filter"); - e.Bounds = AABB2(0.f, 0.f, size.x, 24.f); - @e.Changed = spades::ui::EventHandler(this.OnFilterChanged); - AddChild(e); - @filter = e; - } - - { - StartupScreenConfigView cfg(Manager); - - string[]@ names = GetAllConfigNames(); - - for(uint i = 0, count = names.length; i < count; i++) { - - cfg.AddRow(StartupScreenConfigFieldItemEditor(ui, - StartupScreenConfig(ui, names[i]), names[i], "")); - } - - cfg.Finalize(); - cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); - cfg.Bounds = AABB2(0.f, 30.f, size.x, size.y - 30.f); - AddChild(cfg); - @configView = cfg; - } - } - - private void HandleHelpText(string text) { - helpView.Text = text; - } - - private void OnFilterChanged(spades::ui::UIElement@){ - configView.Filter(filter.Text); - } - - void LoadConfig() { - configView.LoadConfig(); - - } - - - - } - - - StartupScreenUI@ CreateStartupScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, - FontManager@ fontManager, StartupScreenHelper@ helper) { - return StartupScreenUI(renderer, audioDevice, fontManager, helper); - } -} diff --git a/Resources/Scripts/Gui/StartupScreen/ConfigViewFramework.as b/Resources/Scripts/Gui/StartupScreen/ConfigViewFramework.as new file mode 100644 index 00000000..b05f080b --- /dev/null +++ b/Resources/Scripts/Gui/StartupScreen/ConfigViewFramework.as @@ -0,0 +1,754 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "../UIFramework/UIControls.as" + +namespace spades { + + funcdef void HelpTextHandler(string text); + class ChainedEventHandler { + spades::ui::EventHandler@ h; + spades::ui::EventHandler@ h2; + ChainedEventHandler(spades::ui::EventHandler@ h, spades::ui::EventHandler@ h2) { + @this.h = h; + @this.h2 = h2; + } + void Handler(spades::ui::UIElement@ e) { + h(e); h2(e); + } + } + class HelpHandler { + private HelpTextHandler@ handler; + private spades::ui::TextViewer@ helpView; + private string text; + HelpHandler(spades::ui::TextViewer@ helpView, string text) { + this.text = text; + @this.helpView = helpView; + } + HelpHandler(HelpTextHandler@ handler, string text) { + this.text = text; + @this.handler = handler; + } + private void OnMouseHover(spades::ui::UIElement@ elm) { + if(helpView !is null) { + helpView.Text = text; + } + if(handler !is null) { + handler(text); + } + } + void Watch(spades::ui::UIElement@ elm) { + @elm.MouseEntered = spades::ui::EventHandler(this.OnMouseHover); + } + void WatchDeep(spades::ui::UIElement@ elm) { + if(elm.MouseEntered !is null) { + ChainedEventHandler chain(elm.MouseEntered, spades::ui::EventHandler(this.OnMouseHover)); + @elm.MouseEntered = spades::ui::EventHandler(chain.Handler); + }else{ + Watch(elm); + } + spades::ui::UIElementIterator it(elm); + while(it.MoveNext()) { + WatchDeep(it.Current); + } + } + } + + mixin class LabelAddable { + + private void AddLabel(float x, float y, float h, string text) { + spades::ui::Label label(Manager); + Font@ font = ui.fontManager.GuiFont; + Vector2 siz = font.Measure(text); + label.Text = text; + label.Alignment = Vector2(0.f, 0.5f); + label.Bounds = AABB2(x, y, siz.x, h); + AddChild(label); + } + + } + + class StartupScreenConfigViewModel: spades::ui::ListViewModel { + spades::ui::UIElement@[] items; + spades::ui::UIElement@[]@ items2; + StartupScreenConfigViewModel() { + } + int NumRows { + get { + if(items2 !is null) + return int(items2.length); + return int(items.length); + } + } + + void Filter(string text) { + if(text.length == 0) { + @items2 = null; + return; + } + spades::ui::UIElement@[] newItems; + for(uint i = 0, count = items.length; i < count; i++) { + StartupScreenConfigItemEditor@ editor = + cast(items[i]); + if(editor is null) continue; + string label = editor.GetLabel(); + if(StringContainsCaseInsensitive(label, text)) { + newItems.insertLast(items[i]); + } + } + @items2 = newItems; + } + spades::ui::UIElement@ CreateElement(int row) { + if(items2 !is null) + return items2[row]; + return items[row]; + } + void RecycleElement(spades::ui::UIElement@ elem) { + } + } + + interface StartupScreenGenericConfig { + string GetValue(); + void SetValue(string); + /** Returns an empty string when there's no problem. */ + string CheckValueCapability(string); + } + + class StartupScreenConfig: StartupScreenGenericConfig { + private StartupScreenUI@ ui; + private ConfigItem@ cfg; + private string cfgName; + StartupScreenConfig(StartupScreenUI@ ui, string cfg) { + @this.ui = ui; + @this.cfg = ConfigItem(cfg); + cfgName = cfg; + } + string GetValue() { + return cfg.StringValue; + } + void SetValue(string v) { + cfg.StringValue = v; + } + string CheckValueCapability(string v) { + return ui.helper.CheckConfigCapability(cfgName, v); + } + } + + class StartupScreenConfigSetter { + StartupScreenGenericConfig@ c; + string value; + StartupScreenConfigSetter(StartupScreenGenericConfig@ c, string value) { + @this.c = c; + this.value = value; + } + void Set(spades::ui::UIElement@) { + c.SetValue(this.value); + } + } + + interface StartupScreenConfigItemEditor { + void LoadConfig(); + StartupScreenGenericConfig@ GetConfig(); + void SetHelpTextHandler(HelpTextHandler@); + string GetLabel(); + } + + + class StartupScreenConfigSelectItemEditor: spades::ui::UIElement, LabelAddable, StartupScreenConfigItemEditor, StartupScreenConfigItemEditor { + + protected StartupScreenUI@ ui; + private string[]@ descs; + private string[]@ values; + protected StartupScreenGenericConfig@ config; + protected spades::ui::RadioButton@[] buttons; + private string label; + + StartupScreenConfigSelectItemEditor(StartupScreenUI@ ui, + StartupScreenGenericConfig@ cfg, + string values, string descs) { + super(ui.manager); + @this.ui = ui; + @this.descs = descs.split("|"); + @config = cfg; + @this.values = values.split("|"); + + { + string desc = this.descs[0]; + int idx = desc.findFirst(":"); + if(idx >= 0) { + desc = desc.substr(0, idx); + } + AddLabel(0, 0, 24.f, desc); + this.label = desc; + } + + + for(uint i = 0; i < this.values.length; i++) { + spades::ui::RadioButton b(Manager); + string desc = this.descs[i + 1]; + int idx = desc.findFirst(":"); + if(idx >= 0) { + desc = desc.substr(0, idx); + } + b.Caption = desc; + + b.GroupName = "hoge"; + StartupScreenConfigSetter setter(config, this.values[i]); + @b.Activated = spades::ui::EventHandler(setter.Set); + buttons.insertLast(b); + this.AddChild(b); + } + } + + string GetLabel() { + return label; + } + + void LoadConfig() { + string val = config.GetValue(); + for(uint i = 0, count = values.length; i < count; i++) { + buttons[i].Toggled = (values[i] == val); + buttons[i].Enable = CheckValueCapability(values[i]).length == 0; + } + } + string CheckValueCapability(string v) { + return config.CheckValueCapability(v); + } + StartupScreenGenericConfig@ GetConfig() { + return config; + } + void SetHelpTextHandler(HelpTextHandler@ handler) { + for(uint i = 0, count = values.length; i < count; i++) { + string desc = descs[i + 1]; + int idx = desc.findFirst(":"); + if(idx < 0) { + desc = descs[0]; + idx = desc.findFirst(":"); + } + desc = desc.substr(uint(idx + 1)); + HelpHandler(handler, desc).Watch(buttons[i]); + } + } + + void set_Bounds(AABB2 v) { + UIElement::set_Bounds(v); + Vector2 size = this.Size; + float h = 24.f; + float x = size.x; + for(uint i = buttons.length; i > 0; i--) { + spades::ui::RadioButton@ b = buttons[i - 1]; + float w = ui.fontManager.GuiFont.Measure(b.Caption).x + 26.f; + x -= w + 2.f; + b.Bounds = AABB2(x, 0.f, w, h); + } + } + } + + class StartupScreenConfigCheckItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor { + + protected StartupScreenUI@ ui; + private string desc; + private string valueOff; + private string valueOn; + private StartupScreenGenericConfig@ config; + private spades::ui::CheckBox@ button; + private string label; + + StartupScreenConfigCheckItemEditor(StartupScreenUI@ ui, + StartupScreenGenericConfig@ cfg, + string valueOff, string valueOn, string label, string desc) { + super(ui.manager); + @this.ui = ui; + @config = cfg; + this.valueOff = valueOff; + this.valueOn = valueOn; + this.desc = desc; + + spades::ui::CheckBox b(Manager); + b.Caption = label; + this.label = label; + @b.Activated = spades::ui::EventHandler(this.StateChanged); + @button = b; + this.AddChild(b); + } + + string GetLabel() { + return label; + } + + void LoadConfig() { + string val = config.GetValue(); + button.Toggled = (val != valueOff); + button.Enable = CheckValueCapability(valueOn).length == 0; + } + string CheckValueCapability(string v) { + return config.CheckValueCapability(v); + } + StartupScreenGenericConfig@ GetConfig() { + return config; + } + private void StateChanged(spades::ui::UIElement@) { + config.SetValue(button.Toggled ? valueOn : valueOff); + } + void SetHelpTextHandler(HelpTextHandler@ handler) { + HelpHandler(handler, desc).Watch(button); + } + + void set_Bounds(AABB2 v) { + UIElement::set_Bounds(v); + Vector2 size = this.Size; + float h = 24.f; + button.Bounds = AABB2(0.f, 0.f, size.x, h); + } + } + + class StartupScreenConfigSliderItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor, LabelAddable { + + private StartupScreenUI@ ui; + private string desc; + private double stepSize; + private StartupScreenGenericConfig@ config; + private spades::ui::Slider@ slider; + private spades::ui::Label@ valueLabel; + private ConfigNumberFormatter@ formatter; + private string label; + + StartupScreenConfigSliderItemEditor(StartupScreenUI@ ui, + StartupScreenGenericConfig@ cfg, + double minValue, double maxValue, double stepSize, string label, string desc, + ConfigNumberFormatter@ formatter) { + super(ui.manager); + @this.ui = ui; + @config = cfg; + this.desc = desc; + this.stepSize = stepSize; + this.label = label; + + AddLabel(0, 0, 24.f, label); + + spades::ui::Slider b(Manager); + b.MinValue = minValue; + b.MaxValue = maxValue; + + // compute large change + int steps = int((maxValue - minValue) / stepSize); + steps = (steps + 9) / 10; + b.LargeChange = float(steps) * stepSize; + b.SmallChange = stepSize; + + @b.Changed = spades::ui::EventHandler(this.StateChanged); + @slider = b; + this.AddChild(b); + + spades::ui::Label l(Manager); + @valueLabel = l; + l.Alignment = Vector2(1.f, 0.5f); + this.AddChild(l); + + @this.formatter = formatter; + } + + private void UpdateLabel() { + double v = slider.Value; + string s = formatter.Format(v); + valueLabel.Text = s; + } + + string GetLabel() { + return label; + } + + void LoadConfig() { + string val = config.GetValue(); + double v = ParseDouble(val); + // don't use `ScrollTo` here; this calls `Changed` so + // out-of-range value will be clamped and saved back + slider.Value = Clamp(v, slider.MinValue, slider.MaxValue); + // FIXME: button.Enable = CheckValueCapability(valueOn).length == 0; + UpdateLabel(); + } + string CheckValueCapability(string v) { + return ""; //FIXME: config.CheckValueCapability(v); + } + StartupScreenGenericConfig@ GetConfig() { + return config; + } + + void DoRounding() { + double v = double(slider.Value - slider.MinValue); + v = floor((v / stepSize) + 0.5) * stepSize; + v += double(slider.MinValue); + slider.Value = v; + } + private void StateChanged(spades::ui::UIElement@) { + DoRounding(); + config.SetValue(ToString(slider.Value)); + UpdateLabel(); + } + void SetHelpTextHandler(HelpTextHandler@ handler) { + HelpHandler(handler, desc).WatchDeep(slider); + } + + void set_Bounds(AABB2 v) { + UIElement::set_Bounds(v); + Vector2 size = this.Size; + float h = 24.f; + slider.Bounds = AABB2(100.f, 2.f, size.x - 180.f, h - 4.f); + valueLabel.Bounds = AABB2(size.x - 80.f, 0.f, 80.f, h); + } + } + + + class StartupScreenConfigFieldItemEditor: spades::ui::UIElement, StartupScreenConfigItemEditor, LabelAddable { + + private StartupScreenUI@ ui; + private string desc; + private double stepSize; + private StartupScreenGenericConfig@ config; + private spades::ui::Field@ field; + private string label; + + StartupScreenConfigFieldItemEditor(StartupScreenUI@ ui, + StartupScreenGenericConfig@ cfg, + string label, string desc) { + super(ui.manager); + @this.ui = ui; + @config = cfg; + this.desc = desc; + this.label = label; + + AddLabel(0, 0, 24.f, label); + + spades::ui::Field b(Manager); + + @b.Changed = spades::ui::EventHandler(this.StateChanged); + @field = b; + this.AddChild(b); + + } + + string GetLabel() { + return label; + } + + void LoadConfig() { + string val = config.GetValue(); + field.Text = val; + } + string CheckValueCapability(string v) { + return config.CheckValueCapability(v); + } + StartupScreenGenericConfig@ GetConfig() { + return config; + } + + private void StateChanged(spades::ui::UIElement@) { + config.SetValue(field.Text); + } + void SetHelpTextHandler(HelpTextHandler@ handler) { + HelpHandler(handler, desc).WatchDeep(field); + } + + void set_Bounds(AABB2 v) { + UIElement::set_Bounds(v); + Vector2 size = this.Size; + float h = 24.f; + field.Bounds = AABB2(240.f, 0.f, size.x - 240.f, h); + } + } + + // Maps multiple configs to {"0", "1", ...} or "custom" + class StartupScreenComplexConfig: StartupScreenGenericConfig { + StartupScreenConfigItemEditor@[] editors; + StartupScreenComplexConfigPreset@[] presets; + + void AddEditor(StartupScreenConfigItemEditor@ editor) { + Assert(presets.length == 0); + editors.insertLast(editor); + } + void AddPreset(StartupScreenComplexConfigPreset@ preset) { + Assert(preset.Values.length == editors.length); + presets.insertLast(preset); + } + + string GetValue() { + string[] values; + + for(uint i = 0; i < editors.length; i++) { + values.insertLast(editors[i].GetConfig().GetValue()); + } + for(uint i = 0; i < presets.length; i++) { + string[]@ pval = presets[i].Values; + uint j = 0; + uint jc = pval.length; + for(; j < jc; j++) { + if(values[j] != pval[j]) { + break; + } + } + if(j == jc) { + return ToString(int(i)); + } + } + return "custom"; + } + void SetValue(string v) { + if(v == "custom") { + return; + } + uint pId = uint(ParseInt(v)); + string[]@ pval = presets[pId].Values; + for(uint i = 0; i < pval.length; i++) { + editors[i].GetConfig().SetValue(pval[i]); + } + } + string CheckValueCapability(string v) { + if(v == "custom") { + return ""; + } + uint pId = uint(ParseInt(v)); + string[]@ pval = presets[pId].Values; + string ret = ""; + for(uint i = 0; i < pval.length; i++) { + ret += editors[i].GetConfig().CheckValueCapability(pval[i]); + } + return ret; + } + + // used for StartupScreenConfigSelectItemEditor's ctor param + string GetValuesString() { + string[] lst; + for(uint i = 0; i < presets.length; i++) { + lst.insertLast(ToString(int(i))); + } + lst.insertLast("custom"); + return join(lst, "|"); + } + // used for StartupScreenConfigSelectItemEditor's ctor param + string GetDescriptionsString() { + string[] lst; + for(uint i = 0; i < presets.length; i++) { + lst.insertLast(presets[i].Name); + } + lst.insertLast(_Tr("StartupScreen", "Custom")); + return join(lst, "|"); + } + } + + class StartupScreenComplexConfigPreset { + string Name; + string[] Values; + StartupScreenComplexConfigPreset(string name, string values) { + Name = name; + Values = values.split("|"); + } + } + + class StartupScreenConfigComplexItemEditor: StartupScreenConfigSelectItemEditor { + private string dlgTitle; + + StartupScreenConfigComplexItemEditor(StartupScreenUI@ ui, + StartupScreenComplexConfig@ cfg, + string label, string desc) { + super(ui, cfg, cfg.GetValuesString(), + label + ":" + desc + "|" + cfg.GetDescriptionsString()); + dlgTitle = label; + @buttons[buttons.length - 1].Activated = spades::ui::EventHandler(this.CustomClicked); + } + + private void CustomClicked(spades::ui::UIElement@) { + RunDialog(); + } + + private void DialogDone(spades::ui::UIElement@) { + LoadConfig(); + } + + private void RunDialog() { + StartupScreenComplexConfigDialog dlg(ui.manager, cast(config)); + @dlg.DialogDone = spades::ui::EventHandler(this.DialogDone); + dlg.Title = dlgTitle; + dlg.RunDialog(); + } + } + + class StartupScreenConfigView: spades::ui::ListViewBase { + private StartupScreenConfigViewModel vmodel; + + StartupScreenConfigView(spades::ui::UIManager@ manager) { + super(manager); + this.RowHeight = 30.f; + } + void Finalize() { + @this.Model = vmodel; + } + void AddRow(spades::ui::UIElement@ elm) { + vmodel.items.insertLast(elm); + } + + void Filter(string text) { + vmodel.Filter(text); + Reload(); + } + + void SetHelpTextHandler(HelpTextHandler@ handler) { + spades::ui::UIElement@[]@ elms = vmodel.items; + for(uint i = 0; i < elms.length; i++) { + StartupScreenConfigItemEditor@ item = cast(elms[i]); + if(item !is null) { + item.SetHelpTextHandler(handler); + } + } + } + void LoadConfig() { + spades::ui::UIElement@[]@ elms = vmodel.items; + for(uint i = 0; i < elms.length; i++) { + StartupScreenConfigItemEditor@ item = cast(elms[i]); + if(item !is null) { + item.LoadConfig(); + } + } + } + } + + class StartupScreenComplexConfigDialog: spades::ui::UIElement { + StartupScreenComplexConfig@ config; + float ContentsTop = 50.f; + float ContentsHeight; + string Title; + + spades::ui::TextViewer@ helpView; + StartupScreenConfigView@ configView; + + spades::ui::EventHandler@ DialogDone; + + private spades::ui::UIElement@ oldRoot; + + StartupScreenComplexConfigDialog(spades::ui::UIManager@ manager, + StartupScreenComplexConfig@ config) { + super(manager); + @this.config = config; + Vector2 size = manager.RootElement.Size; + ContentsHeight = size.y - ContentsTop * 2.f; + + float mainWidth = size.x - 250.f; + { + spades::ui::TextViewer e(Manager); + e.Bounds = AABB2(mainWidth, ContentsTop + 30.f, size.x - mainWidth - 10.f, ContentsHeight - 60.f); + AddChild(e); + @helpView = e; + } + + { + StartupScreenConfigView cfg(Manager); + + StartupScreenConfigItemEditor@[]@ editors = config.editors; + + for(uint i = 0; i < editors.length; i++) + cfg.AddRow(cast(editors[i])); + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(10.f, ContentsTop + 30.f, mainWidth - 20.f, ContentsHeight - 60.f); + AddChild(cfg); + @configView = cfg; + } + + { + spades::ui::Button button(Manager); + button.Caption = _Tr("StartupScreen", "Close"); + button.Bounds = AABB2(size.x - 160.f, ContentsTop + ContentsHeight - 30.f, 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.CloseActivated); + AddChild(button); + } + + } + + private void CloseActivated(spades::ui::UIElement@) { + if(oldRoot !is null) { + oldRoot.Enable = true; + @oldRoot = null; + } + @this.Parent = null; + DialogDone(this); + } + + private void HandleHelpText(string s) { + helpView.Text = s; + } + + void RunDialog() { + spades::ui::UIElement@ root = Manager.RootElement; + + spades::ui::UIElementIterator iterator(root); + while(iterator.MoveNext()) { + spades::ui::UIElement@ e = iterator.Current; + if(e.Enable and e.Visible) { + @oldRoot = e; + e.Enable = false; + break; + } + } + + this.Bounds = root.Bounds; + root.AddChild(this); + + configView.LoadConfig(); + } + + void Render() { + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Renderer@ r = Manager.Renderer; + Image@ img = r.RegisterImage("Gfx/White.tga"); + + r.ColorNP = Vector4(0, 0, 0, 0.8f); + r.DrawImage(img, + AABB2(pos.x, pos.y, size.x, ContentsTop - 15.f)); + r.DrawImage(img, + AABB2(pos.x, ContentsTop + ContentsHeight + 15.f, size.x, ContentsTop - 15.f)); + + r.ColorNP = Vector4(0, 0, 0, 0.95f); + r.DrawImage(img, + AABB2(pos.x, ContentsTop - 15.f, size.x, ContentsHeight + 30.f)); + + r.ColorNP = Vector4(1, 1, 1, 0.08f); + r.DrawImage(img, + AABB2(pos.x, pos.y + ContentsTop - 15.f, size.x, 1.f)); + r.DrawImage(img, + AABB2(pos.x, pos.y + ContentsTop + ContentsHeight + 15.f, size.x, 1.f)); + r.ColorNP = Vector4(1, 1, 1, 0.2f); + r.DrawImage(img, + AABB2(pos.x, pos.y + ContentsTop - 14.f, size.x, 1.f)); + r.DrawImage(img, + AABB2(pos.x, pos.y + ContentsTop + ContentsHeight + 14.f, size.x, 1.f)); + + Font@ font = Font; + r.ColorNP = Vector4(0.8f, 0.8f, 0.8f, 1.f); + r.DrawImage(img, + AABB2(pos.x, pos.y + ContentsTop, size.x, 20.f)); + font.Draw(Title, Vector2(pos.x + 10.f, pos.y + ContentsTop), 1.f, Vector4(0.f, 0.f, 0.f, 1.f)); + + UIElement::Render(); + } + + } + +} diff --git a/Resources/Scripts/Gui/StartupScreen/ConfigViewTabs.as b/Resources/Scripts/Gui/StartupScreen/ConfigViewTabs.as new file mode 100644 index 00000000..3a3e1970 --- /dev/null +++ b/Resources/Scripts/Gui/StartupScreen/ConfigViewTabs.as @@ -0,0 +1,868 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "ConfigViewFramework.as" +#include "../UIFramework/DropDownList.as" +#include "../MessageBox.as" +#include "UpdateCheckView.as" + +namespace spades { + + class StartupScreenGraphicsTab: spades::ui::UIElement, LabelAddable { + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + StartupScreenGraphicsDisplayResolutionEditor@ resEdit; + + spades::ui::CheckBox@ fullscreenCheck; + spades::ui::RadioButton@ driverOpenGL; + spades::ui::RadioButton@ driverSoftware; + + spades::ui::TextViewer@ helpView; + StartupScreenConfigView@ configViewGL; + StartupScreenConfigView@ configViewSoftware; + + private ConfigItem r_renderer("r_renderer"); + private ConfigItem r_fullscreen("r_fullscreen"); + + StartupScreenGraphicsTab(StartupScreenUI@ ui, Vector2 size) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + + float mainWidth = size.x - 250.f; + + { + spades::ui::TextViewer e(Manager); + e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); + @e.Font = ui.fontManager.GuiFont; + e.Text = _Tr("StartupScreen", "Graphics Settings"); + AddChild(e); + @helpView = e; + } + + AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", "Resolution")); + { + StartupScreenGraphicsDisplayResolutionEditor e(ui); + e.Bounds = AABB2(100.f, 0.f, 124.f, 24.f); + AddChild(e); + @resEdit = e; + } + + { + spades::ui::CheckBox e(Manager); + e.Caption = _Tr("StartupScreen", "Fullscreen Mode"); + e.Bounds = AABB2(230.f, 0.f, 200.f, 24.f); + HelpHandler(helpView, + _Tr("StartupScreen", "By running in fullscreen mode OpenSpades occupies the " + "screen, making it easier for you to concentrate on playing the game.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnFullscreenCheck); + AddChild(e); + @fullscreenCheck = e; + } + + AddLabel(0.f, 30.f, 24.f, _Tr("StartupScreen", "Backend")); + { + spades::ui::RadioButton e(Manager); + e.Caption = _Tr("StartupScreen", "OpenGL"); + e.Bounds = AABB2(100.f, 30.f, 140.f, 24.f); + e.GroupName = "driver"; + HelpHandler(helpView, + _Tr("StartupScreen", "OpenGL renderer uses your computer's graphics " + "accelerator to generate the game screen.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnDriverOpenGL); + AddChild(e); + @driverOpenGL = e; + } + { + spades::ui::RadioButton e(Manager); + e.Caption = _Tr("StartupScreen", "Software"); + e.Bounds = AABB2(250.f, 30.f, 140.f, 24.f); + e.GroupName = "driver"; + HelpHandler(helpView, + _Tr("StartupScreen", "Software renderer uses CPU to generate the game " + "screen. Its quality and performance might be inferior to OpenGL " + "renderer, but it works even with an unsupported GPU.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnDriverSoftware); + AddChild(e); + @driverSoftware = e; + } + + { + StartupScreenConfigView cfg(Manager); + + cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, + StartupScreenGraphicsAntialiasConfig(ui), "0|2|4|fxaa", + _Tr("StartupScreen", + "Antialias:Enables a technique to improve the appearance of high-contrast edges.\n\n" + "MSAA: Performs antialiasing by generating an intermediate high-resolution image. " + "Looks best, but doesn't cope with some settings.\n\n" + "FXAA: Performs antialiasing by smoothing artifacts out as a post-process.|" + "Off|MSAA 2x|4x|FXAA"))); + + cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_radiosity"), "0", "1", _Tr("StartupScreen", "Global Illumination"), + _Tr("StartupScreen", + "Enables a physically based simulation of light path for more realistic lighting."))); + + cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_hdr"), "0", "1", _Tr("StartupScreen", "Linear HDR Rendering"), + _Tr("StartupScreen", + "Uses a number representation which allows wider dynamic range during rendering process. " + "Additionally, this allows color calculation whose value is in linear correspondence with actual energy, " + "that is, physically accurate blending can be achieved."))); + + { + StartupScreenComplexConfig cplx; + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_cameraBlur"), "0", "1", _Tr("StartupScreen", "Camera Blur"), + _Tr("StartupScreen", "Blurs the screen when you turn quickly."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_lens"), "0", "1", _Tr("StartupScreen", "Lens Effect"), + _Tr("StartupScreen", "Simulates distortion caused by a real camera lens."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_bloom"), "0", "1", _Tr("StartupScreen", "Lens Scattering Filter"), + _Tr("StartupScreen", "Simulates light being scattered by dust on the camera lens."))); + // r_lens is currently no-op + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_lensFlare"), "0", "1", _Tr("StartupScreen", "Lens Flare"), + _Tr("StartupScreen", "The Sun causes lens flare."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_lensFlareDynamic"), "0", "1", _Tr("StartupScreen", "Flares for Dynamic Lights"), + _Tr("StartupScreen", "Enables lens flare for light sources other than the Sun."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_colorCorrection"), "0", "1", _Tr("StartupScreen", "Color Correction"), + _Tr("StartupScreen", "Applies cinematic color correction to make the image look better."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_depthOfField"), "0", "1", _Tr("StartupScreen", "Depth of Field"), + _Tr("StartupScreen", "Blurs out-of-focus objects."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_ssao"), "0", "1", _Tr("StartupScreen", "Screen Space Ambient Occlusion"), + _Tr("StartupScreen", "Simulates soft shadows that occur between nearby objects."))); + + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "0|0|0|0|0|0|0|0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Medium"), "1|0|0|1|0|1|0|0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "1|1|1|1|1|1|1|0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Ultra"), "1|1|1|1|1|1|1|1")); + + cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, + _Tr("StartupScreen", "Post-process"), + _Tr("StartupScreen", "Post-process modifies the image to make it look better and " + "more realistic."))); + } + + cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, + StartupScreenConfig(ui, "r_softParticles"), "0|1|2", + _Tr("StartupScreen", + "Particles|" + "Low:Artifact occurs when a particle intersects other objects.|" + "Medium:Particle intersects objects smoothly.|" + "High:Particle intersects objects smoothly, and some objects casts " + "their shadow to particles."))); + + { + StartupScreenComplexConfig cplx; + // r_mapSoftShadow is currently no-op + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_dlights"), "0", "1", _Tr("StartupScreen", "Dynamic Lights"), + _Tr("StartupScreen", + "Gives some objects an ability to emit light to give them " + "an energy-emitting impression."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_modelShadows"), "0", "1", _Tr("StartupScreen", "Shadows"), + _Tr("StartupScreen", "Non-static object casts a shadow."))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_fogShadow"), "0", "1", _Tr("StartupScreen", "Volumetric Fog"), + _Tr("StartupScreen", "Simulates shadow being casted to the fog particles using a " + "super highly computationally demanding algorithm. "))); + cplx.AddEditor(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "r_physicalLighting"), "0", "1", _Tr("StartupScreen", "Physically Based Lighting"), + _Tr("StartupScreen", "Uses more accurate approximation techniques to decide the brightness of objects."))); + + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "1|0|0|0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Medium"), "1|1|0|0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "1|1|0|1")); + + cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, + _Tr("StartupScreen", "Direct Lights"), + _Tr("StartupScreen", "Controls how light encounting a material and atmosphere directly " + "affects its appearance."))); + } + + { + StartupScreenComplexConfig cplx; + + cplx.AddEditor(StartupScreenConfigSelectItemEditor(ui, + StartupScreenConfig(ui, "r_water"), "0|1|2|3", + _Tr("StartupScreen", + "Water Shader|" + "None:Water is rendered in the same way that normal blocks are done.|" + "Level 1:Refraction and the reflected Sun are simulated.|" + "Level 2:Waving water is simulated as well as reflection and refraction.|" + "Level 3:Reflections and refractions are rendered at the highest quality using screen-space techniques."))); + + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Low"), "0")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Med"), "1")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "High"), "2")); + cplx.AddPreset(StartupScreenComplexConfigPreset(_Tr("StartupScreen", "Ultra"), "3")); + + cfg.AddRow(StartupScreenConfigComplexItemEditor(ui, cplx, + _Tr("StartupScreen", "Shader Effects"), _Tr("StartupScreen", "Special effects."))); + } + + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(0.f, 60.f, mainWidth, size.y - 60.f); + AddChild(cfg); + @configViewGL = cfg; + } + + { + StartupScreenConfigView cfg(Manager); + + cfg.AddRow(StartupScreenConfigSelectItemEditor(ui, + StartupScreenConfig(ui, "r_swUndersampling"), "0|1|2", + _Tr("StartupScreen", + "Fast Mode:Reduces the image resolution to make the rendering faster.|" + "Off|2x|4x"))); + + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(0.f, 60.f, mainWidth, size.y - 60.f); + AddChild(cfg); + @configViewSoftware = cfg; + } + + } + + private void HandleHelpText(string text) { + helpView.Text = text; + } + + private void OnDriverOpenGL(spades::ui::UIElement@){ r_renderer.StringValue = "gl"; LoadConfig(); } + private void OnDriverSoftware(spades::ui::UIElement@){ r_renderer.StringValue = "sw"; LoadConfig(); } + + private void OnFullscreenCheck(spades::ui::UIElement@) + { r_fullscreen.IntValue = fullscreenCheck.Toggled ? 1 : 0; } + + void LoadConfig() { + resEdit.LoadConfig(); + if(r_renderer.StringValue == "sw") { + driverSoftware.Check(); + configViewGL.Visible = false; + configViewSoftware.Visible = true; + }else{ + driverOpenGL.Check(); + configViewGL.Visible = true; + configViewSoftware.Visible = false; + } + fullscreenCheck.Toggled = r_fullscreen.IntValue != 0; + driverOpenGL.Enable = ui.helper.CheckConfigCapability("r_renderer", "gl").length == 0; + driverSoftware.Enable = ui.helper.CheckConfigCapability("r_renderer", "sw").length == 0; + configViewGL.LoadConfig(); + configViewSoftware.LoadConfig(); + } + } + + class StartupScreenComboBoxDropdownButton: spades::ui::SimpleButton { + StartupScreenComboBoxDropdownButton(spades::ui::UIManager@ manager) { + super(manager); + } + void Render() { + SimpleButton::Render(); + + Renderer@ renderer = Manager.Renderer; + Image@ image = renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); + AABB2 bnd = ScreenBounds; + Vector2 p = (bnd.min + bnd.max) * 0.5f + Vector2(-8.f, 8.f); + renderer.DrawImage(image, AABB2(p.x, p.y, 16.f, -16.f)); + } + } + + class StartupScreenGraphicsDisplayResolutionEditor: spades::ui::UIElement { + spades::ui::Field@ widthField; + spades::ui::Field@ heightField; + spades::ui::Button@ dropdownButton; + private ConfigItem r_videoWidth("r_videoWidth"); + private ConfigItem r_videoHeight("r_videoHeight"); + StartupScreenHelper@ helper; + + StartupScreenGraphicsDisplayResolutionEditor(StartupScreenUI@ ui) { + super(ui.manager); + @helper = ui.helper; + { + spades::ui::Field e(Manager); + AddChild(e); + e.Bounds = AABB2(0, 0, 45.f, 24.f); + e.DenyNonAscii = true; + @e.Changed = spades::ui::EventHandler(this.ValueEditHandler); + @widthField = e; + } + { + spades::ui::Field e(Manager); + AddChild(e); + e.Bounds = AABB2(53, 0, 45.f, 24.f); + e.DenyNonAscii = true; + @e.Changed = spades::ui::EventHandler(this.ValueEditHandler); + @heightField = e; + } + { + StartupScreenComboBoxDropdownButton e(Manager); + AddChild(e); + e.Bounds = AABB2(100, 0, 24.f, 24.f); + @e.Activated = spades::ui::EventHandler(this.ShowDropdown); + @dropdownButton = e; + } + } + + void LoadConfig() { + widthField.Text = ToString(r_videoWidth.IntValue); + heightField.Text = ToString(r_videoHeight.IntValue); + } + + void SaveConfig() { + int w = ParseInt(widthField.Text); + int h = ParseInt(heightField.Text); + if(w < 640 or h < 480 or w > 8192 or h > 8192) return; + r_videoWidth.IntValue = w; + r_videoHeight.IntValue = h; + } + + private void ValueEditHandler(spades::ui::UIElement@) { + SaveConfig(); + } + + private void DropdownHandler(int index) { + if(index >= 0) { + widthField.Text = ToString(helper.GetVideoModeWidth(index)); + heightField.Text = ToString(helper.GetVideoModeHeight(index)); + + SaveConfig(); + } + } + + private void ShowDropdown(spades::ui::UIElement@) { + string[] items = {}; + int cnt = helper.GetNumVideoModes(); + for(int i = 0; i < cnt; i++) { + int w = helper.GetVideoModeWidth(i); + int h = helper.GetVideoModeHeight(i); + string s = ToString(w) + "x" + ToString(h); + items.insertLast(s); + } + spades::ui::ShowDropDownList(this, items, spades::ui::DropDownListHandler(this.DropdownHandler)); + } + void Render() { + Font@ font = this.Font; + font.Draw("x", Vector2(45.f, 0.f) + ScreenPosition, 1.f, Vector4(1.f, 1.f, 1.f, 1.f)); + UIElement::Render(); + } + } + + class StartupScreenGraphicsAntialiasConfig: StartupScreenGenericConfig { + private StartupScreenUI@ ui; + private ConfigItem@ msaaConfig; + private ConfigItem@ fxaaConfig; + StartupScreenGraphicsAntialiasConfig(StartupScreenUI@ ui) { + @this.ui = ui; + @msaaConfig = ConfigItem("r_multisamples"); + @fxaaConfig = ConfigItem("r_fxaa"); + } + string GetValue() { + if(fxaaConfig.IntValue != 0) { + return "fxaa"; + }else{ + int v = msaaConfig.IntValue; + if(v < 2) return "0"; + else return msaaConfig.StringValue; + } + } + void SetValue(string v) { + if(v == "fxaa") { + msaaConfig.StringValue = "0"; + fxaaConfig.StringValue = "1"; + } else if (v == "0" || v == "1") { + msaaConfig.StringValue = "0"; + fxaaConfig.StringValue = "0"; + } else { + msaaConfig.StringValue = v; + fxaaConfig.StringValue = "0"; + } + } + string CheckValueCapability(string v) { + if(v == "fxaa") { + return ui.helper.CheckConfigCapability("r_multisamples", "0") + + ui.helper.CheckConfigCapability("r_fxaa", "1"); + } else if (v == "0" || v == "1") { + return ui.helper.CheckConfigCapability("r_multisamples", "0") + + ui.helper.CheckConfigCapability("r_fxaa", "0"); + } else { + return ui.helper.CheckConfigCapability("r_multisamples", v) + + ui.helper.CheckConfigCapability("r_fxaa", "0"); + } + + } + } + + + class StartupScreenAudioTab: spades::ui::UIElement, LabelAddable { + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + spades::ui::RadioButton@ driverOpenAL; + spades::ui::RadioButton@ driverYSR; + spades::ui::RadioButton@ driverNull; + + spades::ui::TextViewer@ helpView; + StartupScreenConfigView@ configViewOpenAL; + StartupScreenConfigView@ configViewYSR; + + private ConfigItem s_audioDriver("s_audioDriver"); + private ConfigItem s_eax("s_eax"); + + StartupScreenAudioTab(StartupScreenUI@ ui, Vector2 size) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + + float mainWidth = size.x - 250.f; + + { + spades::ui::TextViewer e(Manager); + e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); + @e.Font = ui.fontManager.GuiFont; + e.Text = _Tr("StartupScreen", "Audio Settings"); + AddChild(e); + @helpView = e; + } + + + AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", "Backend")); + { + spades::ui::RadioButton e(Manager); + e.Caption = _Tr("StartupScreen", "OpenAL"); + e.Bounds = AABB2(100.f, 0.f, 100.f, 24.f); + e.GroupName = "driver"; + HelpHandler(helpView, + _Tr("StartupScreen", "Uses an OpenAL-capable sound card to process sound. " + "In most cases where there isn't such a sound card, software emulation is " + "used.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnDriverOpenAL); + AddChild(e); + @driverOpenAL = e; + } + { + spades::ui::RadioButton e(Manager); + e.Caption = _Tr("StartupScreen", "YSR"); + e.Bounds = AABB2(210.f, 0.f, 100.f, 24.f); + e.GroupName = "driver"; + HelpHandler(helpView, + _Tr("StartupScreen", "YSR is an experimental 3D HDR sound engine optimized " + "for OpenSpades. It features several enhanced features including " + "automatic load control, dynamics compressor, HRTF-based " + "3D audio, and high quality reverb.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnDriverYSR); + AddChild(e); + @driverYSR = e; + } + { + spades::ui::RadioButton e(Manager); + //! The name of audio driver that outputs no audio. + e.Caption = _Tr("StartupScreen", "Null"); + e.Bounds = AABB2(320.f, 0.f, 100.f, 24.f); + e.GroupName = "driver"; + HelpHandler(helpView, + _Tr("StartupScreen", "Disables audio output.")).Watch(e); + @e.Activated = spades::ui::EventHandler(this.OnDriverNull); + AddChild(e); + @driverNull = e; + } + + { + StartupScreenConfigView cfg(Manager); + + cfg.AddRow(StartupScreenConfigSliderItemEditor(ui, + StartupScreenConfig(ui, "s_maxPolyphonics"), 16.0, 256.0, 8.0, + _Tr("StartupScreen", "Polyphonics"), _Tr("StartupScreen", + "Specifies how many sounds can be played simultaneously. " + "Higher value needs more processing power, so setting this too high might " + "cause an overload (especially with a software emulation)."), + ConfigNumberFormatter(0, " poly"))); + + cfg.AddRow(StartupScreenConfigCheckItemEditor(ui, + StartupScreenConfig(ui, "s_eax"), "0", "1", + _Tr("StartupScreen", "EAX"), _Tr("StartupScreen", + "Enables extended features provided by the OpenAL driver to create " + "more ambience."))); + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(0.f, 30.f, mainWidth, size.y - 30.f); + AddChild(cfg); + @configViewOpenAL = cfg; + } + + { + StartupScreenConfigView cfg(Manager); + + cfg.AddRow(StartupScreenConfigSliderItemEditor(ui, + StartupScreenConfig(ui, "s_maxPolyphonics"), 16.0, 256.0, 8.0, + _Tr("StartupScreen", "Polyphonics"), _Tr("StartupScreen", + "Specifies how many sounds can be played simultaneously. " + "No matter what value is set, YSR might reduce the number of sounds " + "when an overload is detected."), + ConfigNumberFormatter(0, " poly"))); + + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(0.f, 30.f, mainWidth, size.y - 30.f); + AddChild(cfg); + @configViewYSR = cfg; + } + + } + + private void HandleHelpText(string text) { + helpView.Text = text; + } + + private void OnDriverOpenAL(spades::ui::UIElement@){ s_audioDriver.StringValue = "openal"; LoadConfig(); } + private void OnDriverYSR(spades::ui::UIElement@){ s_audioDriver.StringValue = "ysr"; LoadConfig(); } + private void OnDriverNull(spades::ui::UIElement@){ s_audioDriver.StringValue = "null"; LoadConfig(); } + + void LoadConfig() { + if(s_audioDriver.StringValue == "ysr") { + driverYSR.Check(); + configViewOpenAL.Visible = false; + configViewYSR.Visible = true; + }else if(s_audioDriver.StringValue == "openal"){ + driverOpenAL.Check(); + configViewOpenAL.Visible = true; + configViewYSR.Visible = false; + }else if(s_audioDriver.StringValue == "null"){ + driverNull.Check(); + configViewOpenAL.Visible = false; + configViewYSR.Visible = false; + } + driverOpenAL.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "openal").length == 0; + driverYSR.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "ysr").length == 0; + driverNull.Enable = ui.helper.CheckConfigCapability("s_audioDriver", "null").length == 0; + configViewOpenAL.LoadConfig(); + configViewYSR.LoadConfig(); + + } + + + + } + + class StartupScreenGenericTab: spades::ui::UIElement, LabelAddable { + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + StartupScreenLocaleEditor@ locale; + + StartupScreenGenericTab(StartupScreenUI@ ui, Vector2 size) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + + string label = _Tr("StartupScreen", "Language"); + if (label != "Language") { + label += " (Language)"; + } + AddLabel(0.f, 0.f, 24.f, _Tr("StartupScreen", label)); + { + StartupScreenLocaleEditor e(ui); + AddChild(e); + e.Bounds = AABB2(160.f, 0.f, 400.f, 24.f); + @locale = e; + } + + AddLabel(0.f, 30.f, 30.f, _Tr("StartupScreen", "Tools")); + { + spades::ui::Button button(Manager); + button.Caption = _Tr("StartupScreen", "Reset All Settings"); + button.Bounds = AABB2(160.f, 30.f, 350.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnResetSettingsPressed); + AddChild(button); + } + { + spades::ui::Button button(Manager); + string osType = helper.OperatingSystemType; + if (osType == "Windows") { + button.Caption = _Tr("StartupScreen", "Open Config Folder in Explorer"); + } else if (osType == "Mac") { + button.Caption = _Tr("StartupScreen", "Reveal Config Folder in Finder"); + } else { + button.Caption = _Tr("StartupScreen", "Browse Config Folder"); + } + button.Bounds = AABB2(160.f, 66.f, 350.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnBrowseUserDirectoryPressed); + AddChild(button); + } + } + + void LoadConfig() { + locale.LoadConfig(); + } + + private void OnBrowseUserDirectoryPressed(spades::ui::UIElement@) { + if (helper.BrowseUserDirectory()) { + return; + } + + string msg = _Tr("StartupScreen", "An unknown error has occurred while opening the config directory."); + AlertScreen al(Parent, msg, 100.f); + al.Run(); + } + + private void OnResetSettingsPressed(spades::ui::UIElement@) { + string msg = _Tr("StartupScreen", "Are you sure to reset all settings? They include (but are not limited to):") + "\n" + + "- " + _Tr("StartupScreen", "All graphics/audio settings") + "\n" + + "- " + _Tr("StartupScreen", "All key bindings") + "\n" + + "- " + _Tr("StartupScreen", "Your player name") + "\n" + + "- " + _Tr("StartupScreen", "Other advanced settings only accessible through '{0}' tab and in-game commands", + _Tr("StartupScreen", "Advanced")); + ConfirmScreen al(Parent, msg, 200.f); + @al.Closed = spades::ui::EventHandler(OnResetSettingsConfirmed); + al.Run(); + } + + private void OnResetSettingsConfirmed(spades::ui::UIElement@ sender) { + if (!cast(sender).Result) { + return; + } + + ResetAllSettings(); + + // Reload the startup screen so the language config takes effect + ui.Reload(); + } + + private void ResetAllSettings() { + string[]@ names = GetAllConfigNames(); + + for(uint i = 0, count = names.length; i < count; i++) { + ConfigItem item(names[i]); + item.StringValue = item.DefaultValue; + } + + // Some of default values may be infeasible for the user's system. + helper.FixConfigs(); + } + + } + + class StartupScreenDropdownListDropdownButton: spades::ui::SimpleButton { + StartupScreenDropdownListDropdownButton(spades::ui::UIManager@ manager) { + super(manager); + Alignment = Vector2(0.f, 0.5f); + } + void Render() { + SimpleButton::Render(); + + Renderer@ renderer = Manager.Renderer; + Image@ arrowImg = renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); + + AABB2 bnd = ScreenBounds; + Vector2 p = (bnd.min + bnd.max) * 0.5f + Vector2(-8.f, 8.f); + renderer.DrawImage(arrowImg, AABB2(bnd.max.x - 16.f, p.y, 16.f, -16.f)); + } + } + + class StartupScreenLocaleEditor: spades::ui::UIElement, LabelAddable {StartupScreenUI@ ui; + StartupScreenHelper@ helper; + private ConfigItem core_locale("core_locale"); + + spades::ui::Button@ dropdownButton; + + StartupScreenLocaleEditor(StartupScreenUI@ ui) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + { + StartupScreenDropdownListDropdownButton e(Manager); + AddChild(e); + e.Bounds = AABB2(0.f, 0.f, 400.f, 24.f); + @e.Activated = spades::ui::EventHandler(this.ShowDropdown); + @dropdownButton = e; + } + } + + void LoadConfig() { + string locale = core_locale.StringValue; + string name = _Tr("StartupScreen", "Unknown ({0})", locale); + if (locale == "") { + name = _Tr("StartupScreen", "System default"); + } + + int cnt = helper.GetNumLocales(); + for(int i = 0; i < cnt; i++) { + if (locale == helper.GetLocale(i)) { + name = helper.GetLocaleDescriptionNative(i) + " / " + helper.GetLocaleDescriptionEnglish(i); + } + } + + dropdownButton.Caption = name; + } + + private void ShowDropdown(spades::ui::UIElement@) { + string[] items = {_Tr("StartupScreen", "System default")}; + int cnt = helper.GetNumLocales(); + for(int i = 0; i < cnt; i++) { + string s = helper.GetLocaleDescriptionNative(i) + " / " + helper.GetLocaleDescriptionEnglish(i); + items.insertLast(s); + } + spades::ui::ShowDropDownList(this, items, spades::ui::DropDownListHandler(this.DropdownHandler)); + } + + private void DropdownHandler(int index) { + if(index >= 0) { + if (index == 0) { + core_locale = ""; + } else { + core_locale = helper.GetLocale(index - 1); + } + + // Reload the startup screen so the language config takes effect + ui.Reload(); + } + } + } + + class StartupScreenSystemInfoTab: spades::ui::UIElement, LabelAddable { + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + spades::ui::TextViewer@ helpView; + + StartupScreenSystemInfoTab(StartupScreenUI@ ui, Vector2 size) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + + { + spades::ui::TextViewer e(Manager); + e.Bounds = AABB2(0.f, 0.f, size.x, size.y - 30.f); + @e.Font = ui.fontManager.GuiFont; + AddChild(e); + for(int i = 0, count = helper.GetNumReportLines(); i < count; i++) { + string text = helper.GetReportLineText(i); + Vector4 col = helper.GetReportLineColor(i); + e.AddLine(text, true, col); + } + @helpView = e; + } + + { + spades::ui::Button button(Manager); + button.Caption = _Tr("StartupScreen", "Copy to Clipboard"); + button.Bounds = AABB2(size.x - 180.f, size.y - 30.f, 180.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnCopyReport); + AddChild(button); + } + + } + private void OnCopyReport(spades::ui::UIElement@){ + Manager.Copy(helper.GetReport()); + } + + + + } + + + class StartupScreenAdvancedTab: spades::ui::UIElement, LabelAddable { + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + spades::ui::Field@ filter; + + spades::ui::TextViewer@ helpView; + StartupScreenConfigView@ configView; + + StartupScreenAdvancedTab(StartupScreenUI@ ui, Vector2 size) { + super(ui.manager); + @this.ui = ui; + @helper = ui.helper; + + float mainWidth = size.x - 250.f; + + { + spades::ui::TextViewer e(Manager); + e.Bounds = AABB2(mainWidth + 10.f, 0.f, size.x - mainWidth - 10.f, size.y); + @e.Font = ui.fontManager.GuiFont; + e.Text = _Tr("StartupScreen", "Advanced Settings"); + e.Visible = false; + AddChild(e); + @helpView = e; + } + + + { + spades::ui::Field e(Manager); + e.Placeholder = _Tr("StartupScreen", "Filter"); + e.Bounds = AABB2(0.f, 0.f, size.x, 24.f); + @e.Changed = spades::ui::EventHandler(this.OnFilterChanged); + AddChild(e); + @filter = e; + } + + { + StartupScreenConfigView cfg(Manager); + + string[]@ names = GetAllConfigNames(); + + for(uint i = 0, count = names.length; i < count; i++) { + + cfg.AddRow(StartupScreenConfigFieldItemEditor(ui, + StartupScreenConfig(ui, names[i]), names[i], "")); + } + + cfg.Finalize(); + cfg.SetHelpTextHandler(HelpTextHandler(this.HandleHelpText)); + cfg.Bounds = AABB2(0.f, 30.f, size.x, size.y - 30.f); + AddChild(cfg); + @configView = cfg; + } + } + + private void HandleHelpText(string text) { + helpView.Text = text; + } + + private void OnFilterChanged(spades::ui::UIElement@){ + configView.Filter(filter.Text); + } + + void LoadConfig() { + configView.LoadConfig(); + + } + + + + } + +} diff --git a/Resources/Scripts/Gui/StartupScreen/MainMenu.as b/Resources/Scripts/Gui/StartupScreen/MainMenu.as new file mode 100644 index 00000000..f31c685c --- /dev/null +++ b/Resources/Scripts/Gui/StartupScreen/MainMenu.as @@ -0,0 +1,226 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "UpdateCheckView.as" +#include "ConfigViewTabs.as" + +namespace spades { + + class StartupScreenMainMenuState { + int ActiveTabIndex; + } + + class StartupScreenMainMenu: spades::ui::UIElement { + + StartupScreenUI@ ui; + StartupScreenHelper@ helper; + + spades::ui::ListView@ serverList; + spades::ui::CheckBox@ bypassStartupWindowCheck; + + StartupScreenGraphicsTab@ graphicsTab; + StartupScreenAudioTab@ audioTab; + StartupScreenGenericTab@ genericTab; + StartupScreenSystemInfoTab@ systemInfoTab; + StartupScreenAdvancedTab@ advancedTab; + + private ConfigItem cl_showStartupWindow("cl_showStartupWindow", "-1"); + private bool advancedTabVisible = false; + + StartupScreenMainMenu(StartupScreenUI@ ui) { + super(ui.manager); + @this.ui = ui; + @this.helper = ui.helper; + + @this.Font = ui.fontManager.GuiFont; + + float width = Manager.Renderer.ScreenWidth; + float height = Manager.Renderer.ScreenHeight; + { + spades::ui::Button button(Manager); + button.Caption = _Tr("StartupScreen", "Start"); + button.Bounds = AABB2(width - 170.f, 20.f, 150.f, 30.f); + @button.Activated = spades::ui::EventHandler(this.OnStartPressed); + AddChild(button); + } + { + spades::ui::CheckBox button(Manager); + button.Caption = _Tr("StartupScreen", "Skip this screen next time"); + button.Bounds = AABB2(360.f, 62.f, width - 380.f, 20.f); // note: this is updated later soon + AddChild(button); + @bypassStartupWindowCheck = button; + @button.Activated = spades::ui::EventHandler(this.OnBypassStartupWindowCheckChanged); + } + { + UpdateCheckView view(Manager, ui.helper.PackageUpdateManager); + view.Bounds = AABB2(0.f, height - 40.f, width, 40.f); + @view.OpenUpdateInfoURL = spades::ui::EventHandler(this.OnShowUpdateDetailsPressed); + AddChild(view); + } + + AABB2 clientArea(10.f, 100.f, width - 20.f, height - 150.f); + StartupScreenGraphicsTab graphicsTab(ui, clientArea.max - clientArea.min); + StartupScreenAudioTab audioTab(ui, clientArea.max - clientArea.min); + StartupScreenGenericTab genericTab(ui, clientArea.max - clientArea.min); + StartupScreenSystemInfoTab profileTab(ui, clientArea.max - clientArea.min); + StartupScreenAdvancedTab advancedTab(ui, clientArea.max - clientArea.min); + graphicsTab.Bounds = clientArea; + audioTab.Bounds = clientArea; + genericTab.Bounds = clientArea; + profileTab.Bounds = clientArea; + advancedTab.Bounds = clientArea; + AddChild(graphicsTab); + AddChild(audioTab); + AddChild(genericTab); + AddChild(profileTab); + AddChild(advancedTab); + audioTab.Visible = false; + profileTab.Visible = false; + genericTab.Visible = false; + advancedTab.Visible = false; + + @this.graphicsTab = graphicsTab; + @this.audioTab = audioTab; + @this.advancedTab = advancedTab; + @this.systemInfoTab = profileTab; + @this.genericTab = genericTab; + + { + spades::ui::SimpleTabStrip tabStrip(Manager); + AddChild(tabStrip); + tabStrip.Bounds = AABB2(10.f, 70.f, width - 20.f, 24.f); + tabStrip.AddItem(_Tr("StartupScreen", "Graphics"), graphicsTab); + tabStrip.AddItem(_Tr("StartupScreen", "Audio"), audioTab); + tabStrip.AddItem(_Tr("StartupScreen", "Generic"), genericTab); + tabStrip.AddItem(_Tr("StartupScreen", "System Info"), profileTab); + tabStrip.AddItem(_Tr("StartupScreen", "Advanced"), advancedTab); + @tabStrip.Changed = spades::ui::EventHandler(this.OnTabChanged); + + // Reposition the "Skip this screen next time" check box + spades::ui::UIElement@[]@ tabStripItems = tabStrip.GetChildren(); + float right = tabStripItems[tabStripItems.length - 1].Bounds.max.x + + tabStrip.Bounds.min.x + 10.f; + bypassStartupWindowCheck.Bounds = AABB2(right, 62.f, width - right - 20.f, 20.f); + } + + LoadConfig(); + } + + private void OnTabChanged(spades::ui::UIElement@) { + bool v = advancedTab.Visible; + if(advancedTabVisible and not v) { + LoadConfig(); + } + advancedTabVisible = v; + } + + void LoadConfig() { + switch(cl_showStartupWindow.IntValue) { + case -1: + bypassStartupWindowCheck.Toggled = false; + break; + case 0: + bypassStartupWindowCheck.Toggled = true; + break; + default: + bypassStartupWindowCheck.Toggled = false; + break; + } + + this.graphicsTab.LoadConfig(); + this.audioTab.LoadConfig(); + this.genericTab.LoadConfig(); + this.advancedTab.LoadConfig(); + } + + StartupScreenMainMenuState@ GetState() { + StartupScreenMainMenuState state; + if (this.graphicsTab.Visible) { + state.ActiveTabIndex = 0; + } else if (this.audioTab.Visible) { + state.ActiveTabIndex = 1; + } else if (this.genericTab.Visible) { + state.ActiveTabIndex = 2; + } else if (this.systemInfoTab.Visible) { + state.ActiveTabIndex = 3; + } else if (this.advancedTab.Visible) { + state.ActiveTabIndex = 4; + } + return state; + } + + void SetState(StartupScreenMainMenuState@ state) { + this.graphicsTab.Visible = state.ActiveTabIndex == 0; + this.audioTab.Visible = state.ActiveTabIndex == 1; + this.genericTab.Visible = state.ActiveTabIndex == 2; + this.systemInfoTab.Visible = state.ActiveTabIndex == 3; + this.advancedTab.Visible = state.ActiveTabIndex == 4; + } + + private void OnBypassStartupWindowCheckChanged(spades::ui::UIElement@ sender) { + cl_showStartupWindow.IntValue = (bypassStartupWindowCheck.Toggled ? 0 : 1); + } + + private void OnShowUpdateDetailsPressed(spades::ui::UIElement@ sender) { + if (ui.helper.OpenUpdateInfoURL()) { + return; + } + + string msg = _Tr("StartupScreen", "An unknown error has occurred while opening the update info website."); + msg += "\n\n" + ui.helper.PackageUpdateManager.LatestVersionInfoPageURL; + AlertScreen al(Parent, msg, 100.f); + al.Run(); + } + + private void OnQuitPressed(spades::ui::UIElement@ sender) { + ui.shouldExit = true; + } + + private void OnSetupPressed(spades::ui::UIElement@ sender) { + PreferenceView al(this, PreferenceViewOptions(), ui.fontManager); + al.Run(); + } + + private void Start() { + helper.Start(); + ui.shouldExit = true; // we have to exit from the startup screen to start the game + } + + private void OnStartPressed(spades::ui::UIElement@ sender) { + Start(); + } + + void HotKey(string key) { + if(IsEnabled and key == "Enter") { + Start(); + } else if(IsEnabled and key == "Escape") { + ui.shouldExit = true; + } else { + UIElement::HotKey(key); + } + } + + void Render() { + UIElement::Render(); + + } + } + +} diff --git a/Resources/Scripts/Gui/StartupScreen/StartupScreenUI.as b/Resources/Scripts/Gui/StartupScreen/StartupScreenUI.as new file mode 100644 index 00000000..71e55410 --- /dev/null +++ b/Resources/Scripts/Gui/StartupScreen/StartupScreenUI.as @@ -0,0 +1,131 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "MainMenu.as" + +namespace spades { + + + class StartupScreenUI { + private Renderer@ renderer; + private AudioDevice@ audioDevice; + FontManager@ fontManager; + StartupScreenHelper@ helper; + + spades::ui::UIManager@ manager; + + StartupScreenMainMenu@ mainMenu; + + bool shouldExit = false; + + StartupScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, StartupScreenHelper@ helper) { + @this.renderer = renderer; + @this.audioDevice = audioDevice; + @this.fontManager = fontManager; + @this.helper = helper; + + SetupRenderer(); + + @manager = spades::ui::UIManager(renderer, audioDevice); + @manager.RootElement.Font = fontManager.GuiFont; + Init(); + } + + private void Init() { + @mainMenu = StartupScreenMainMenu(this); + mainMenu.Bounds = manager.RootElement.Bounds; + manager.RootElement.AddChild(mainMenu); + } + + void Reload() { + // Delete StartupScreenMainMenu + @manager.RootElement.GetChildren()[0].Parent = null; + + // Reload entire the startup screen while preserving the state as much as possible + auto@ state = mainMenu.GetState(); + Init(); + mainMenu.SetState(state); + } + + void SetupRenderer() { + if(manager !is null) + manager.KeyPanic(); + } + + void MouseEvent(float x, float y) { + manager.MouseEvent(x, y); + } + + void WheelEvent(float x, float y) { + manager.WheelEvent(x, y); + } + + void KeyEvent(string key, bool down) { + manager.KeyEvent(key, down); + } + + void TextInputEvent(string text) { + manager.TextInputEvent(text); + } + + void TextEditingEvent(string text, int start, int len) { + manager.TextEditingEvent(text, start, len); + } + + bool AcceptsTextInput() { + return manager.AcceptsTextInput; + } + + AABB2 GetTextInputRect() { + return manager.TextInputRect; + } + + void RunFrame(float dt) { + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 1.f); + renderer.DrawImage(renderer.RegisterImage("Gfx/White.tga"), + AABB2(0.f, 0.f, renderer.ScreenWidth, renderer.ScreenHeight)); + + // draw title logo + Image@ img = renderer.RegisterImage("Gfx/Title/LogoSmall.png"); + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 1.f); + renderer.DrawImage(img, AABB2(10.f, 10.f, img.Width, img.Height)); + + manager.RunFrame(dt); + manager.Render(); + + renderer.FrameDone(); + renderer.Flip(); + } + + void Closing() { + shouldExit = true; + } + + bool WantsToBeClosed() { + return shouldExit; + } + } + + StartupScreenUI@ CreateStartupScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, + FontManager@ fontManager, StartupScreenHelper@ helper) { + return StartupScreenUI(renderer, audioDevice, fontManager, helper); + } + +} diff --git a/Resources/Scripts/Gui/UpdateCheckView.as b/Resources/Scripts/Gui/StartupScreen/UpdateCheckView.as similarity index 99% rename from Resources/Scripts/Gui/UpdateCheckView.as rename to Resources/Scripts/Gui/StartupScreen/UpdateCheckView.as index 4642d65b..ddb9ce1b 100644 --- a/Resources/Scripts/Gui/UpdateCheckView.as +++ b/Resources/Scripts/Gui/StartupScreen/UpdateCheckView.as @@ -18,7 +18,7 @@ */ -#include "UIControls.as" +#include "../UIFramework/UIControls.as" namespace spades { class UpdateCheckView: spades::ui::UIElement { @@ -150,4 +150,4 @@ namespace spades { OpenUpdateInfoURL(this); } } -} \ No newline at end of file +} diff --git a/Resources/Scripts/Gui/UIControls.as b/Resources/Scripts/Gui/UIControls.as deleted file mode 100644 index 73d3dd1b..00000000 --- a/Resources/Scripts/Gui/UIControls.as +++ /dev/null @@ -1,1776 +0,0 @@ -/* - Copyright (c) 2013 yvt - - This file is part of OpenSpades. - - OpenSpades is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - OpenSpades is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with OpenSpades. If not, see . - - */ - -namespace spades { - namespace ui { - - class Label: UIElement { - string Text; - Vector4 BackgroundColor = Vector4(0, 0, 0, 0); - Vector4 TextColor = Vector4(1, 1, 1, 1); - Vector2 Alignment = Vector2(0.f, 0.0f); - float TextScale = 1.f; - - Label(UIManager@ manager) { - super(manager); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - - if(BackgroundColor.w > 0.f) { - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.ColorNP = BackgroundColor; - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - } - - if(Text.length > 0) { - Font@ font = this.Font; - string text = this.Text; - Vector2 txtSize = font.Measure(text) * TextScale; - Vector2 txtPos; - txtPos = pos + (size - txtSize) * Alignment; - - font.Draw(text, txtPos, TextScale, TextColor); - } - } - } - - class ButtonBase: UIElement { - bool Pressed = false; - bool Hover = false; - bool Toggled = false; - - bool Toggle = false; - bool Repeat = false; - bool ActivateOnMouseDown = false; - - EventHandler@ Activated; - EventHandler@ DoubleClicked; - EventHandler@ RightClicked; - string Caption; - string ActivateHotKey; - - private Timer@ repeatTimer; - - // for double click detection - private float lastActivate = -1.f; - private Vector2 lastActivatePosition = Vector2(); - - ButtonBase(UIManager@ manager) { - super(manager); - IsMouseInteractive = true; - @repeatTimer = Timer(Manager); - @repeatTimer.Tick = TimerTickEventHandler(this.RepeatTimerFired); - } - - void PlayMouseEnterSound() { - Manager.PlaySound("Sounds/Feedback/Limbo/Hover.opus"); - } - - void PlayActivateSound() { - Manager.PlaySound("Sounds/Feedback/Limbo/Select.opus"); - } - - void OnActivated() { - if(Activated !is null) { - Activated(this); - } - } - - void OnDoubleClicked() { - if(DoubleClicked !is null) { - DoubleClicked(this); - } - } - - void OnRightClicked() { - if(RightClicked !is null) { - RightClicked(this); - } - } - - private void RepeatTimerFired(Timer@ timer) { - OnActivated(); - timer.Interval = 0.1f; - } - - void MouseDown(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton && - button != spades::ui::MouseButton::RightMouseButton) { - return; - } - - PlayActivateSound(); - if (button == spades::ui::MouseButton::RightMouseButton) { - OnRightClicked(); - return; - } - - Pressed = true; - Hover = true; - - if(Repeat or ActivateOnMouseDown) { - OnActivated(); - if(Repeat) { - repeatTimer.Interval = 0.3f; - repeatTimer.Start(); - } - } - } - void MouseMove(Vector2 clientPosition) { - if(Pressed) { - bool newHover = AABB2(Vector2(0.f, 0.f), Size).Contains(clientPosition); - if(newHover != Hover) { - if(Repeat) { - if(newHover) { - OnActivated(); - repeatTimer.Interval = 0.3f; - repeatTimer.Start(); - } else { - repeatTimer.Stop(); - } - } - Hover = newHover; - } - } - } - void MouseUp(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton && - button != spades::ui::MouseButton::RightMouseButton) { - return; - } - if(Pressed) { - Pressed = false; - if(Hover and not (Repeat or ActivateOnMouseDown)) { - if(Toggle) { - Toggled = not Toggled; - } - OnActivated(); - if (Manager.Time < lastActivate + 0.35 && - (clientPosition - lastActivatePosition).ManhattanLength < 10.0f) { - OnDoubleClicked(); - } - lastActivate = Manager.Time; - lastActivatePosition = clientPosition; - } - - if(Repeat and Hover){ - repeatTimer.Stop(); - } - } - } - void MouseEnter() { - Hover = true; - if(not Pressed) { - PlayMouseEnterSound(); - } - UIElement::MouseEnter(); - } - void MouseLeave() { - Hover = false; - UIElement::MouseLeave(); - } - - void KeyDown(string key) { - if(key == " ") { - OnActivated(); - } - UIElement::KeyDown(key); - } - void KeyUp(string key) { - UIElement::KeyUp(key); - } - - void HotKey(string key) { - if(key == ActivateHotKey) { - OnActivated(); - } - } - - } - - class SimpleButton: spades::ui::Button { - SimpleButton(spades::ui::UIManager@ manager){ - super(manager); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - if((Pressed && Hover) || Toggled) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - if((Pressed && Hover) || Toggled) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, 1.f, size.y)); - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x+size.x-1.f, pos.y, 1.f, size.y)); - renderer.DrawImage(img, AABB2(pos.x, pos.y+size.y-1.f, size.x, 1.f)); - Vector2 txtSize = Font.Measure(Caption); - float margin = 4.f; - Font.DrawShadow(Caption, pos + Vector2(margin, margin) + - (size - txtSize - Vector2(margin * 2.f, margin * 2.f)) * Alignment, - 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.4f)); - } - } - - - class CheckBox: spades::ui::Button { - CheckBox(spades::ui::UIManager@ manager){ - super(manager); - this.Toggle = true; - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - if(Pressed && Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.00f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - Vector2 txtSize = Font.Measure(Caption); - Font.DrawShadow(Caption, pos + (size - txtSize) * Vector2(0.f, 0.5f) + Vector2(16.f, 0.f), - 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.2f)); - - @img = renderer.RegisterImage("Gfx/UI/CheckBox.png"); - - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, Toggled ? .9f : 0.6f); - renderer.DrawImage(img, AABB2(pos.x, pos.y + (size.y - 16.f) * 0.5f, 16.f, 16.f), - AABB2(Toggled ? 16.f : 0.f, 0.f, 16.f, 16.f)); - - } - } - - - class RadioButton: spades::ui::Button { - string GroupName; - - RadioButton(spades::ui::UIManager@ manager){ - super(manager); - this.Toggle = true; - } - void Check() { - this.Toggled = true; - - // uncheck others - if(GroupName.length > 0) { - UIElement@[]@ children = this.Parent.GetChildren(); - for(uint i = 0, count = children.length; i < children.length; i++) { - RadioButton@ btn = cast(children[i]); - if(btn is this) continue; - if(btn !is null) { - if(GroupName == btn.GroupName) { - btn.Toggled = false; - } - } - } - } - } - void OnActivated() { - Check(); - - Button::OnActivated(); - } - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - if(!this.Enable) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); - } else if((Pressed && Hover) || Toggled) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - if(!this.Enable) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); - } if((Pressed && Hover) || Toggled) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, 1.f, size.y)); - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x+size.x-1.f, pos.y, 1.f, size.y)); - renderer.DrawImage(img, AABB2(pos.x, pos.y+size.y-1.f, size.x, 1.f)); - Vector2 txtSize = Font.Measure(Caption); - Font.DrawShadow(Caption, pos + (size - txtSize) * 0.5f + Vector2(8.f, 0.f), 1.f, - Vector4(1,1,1,this.Enable ? 1.f : 0.4f), Vector4(0,0,0,0.4f)); - - if(Toggled) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.6f); - renderer.DrawImage(img, AABB2(pos.x + 4.f, pos.y + (size.y - 8.f) * 0.5f, 8.f, 8.f)); - } - - - } - } - - class Button: ButtonBase { - private Image@ image; - Vector2 Alignment = Vector2(0.5f, 0.5f); - - Button(UIManager@ manager) { - super(manager); - - Renderer@ renderer = Manager.Renderer; - @image = renderer.RegisterImage("Gfx/UI/Button.png"); - } - - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - - Vector4 color = Vector4(0.2f, 0.2f, 0.2f, 0.5f); - if(Toggled or (Pressed and Hover)) { - color = Vector4(0.7f, 0.7f, 0.7f, 0.9f); - }else if(Hover) { - color = Vector4(0.4f, 0.4f, 0.4f, 0.7f); - } - if(!IsEnabled) { - color.w *= 0.5f; - } - renderer.ColorNP = color; - - DrawSliceImage(renderer, image, pos.x, pos.y, size.x, size.y, 12.f); - - Font@ font = this.Font; - string text = this.Caption; - Vector2 txtSize = font.Measure(text); - Vector2 txtPos; - pos += Vector2(8.f, 8.f); - size -= Vector2(16.f, 16.f); - txtPos = pos + (size - txtSize) * Alignment; - - if(IsEnabled){ - font.DrawShadow(text, txtPos, 1.f, - Vector4(1.f, 1.f, 1.f, 1.f), Vector4(0.f, 0.f, 0.f, 0.4f)); - }else{ - font.DrawShadow(text, txtPos, 1.f, - Vector4(1.f, 1.f, 1.f, 0.5f), Vector4(0.f, 0.f, 0.f, 0.1f)); - } - } - - } - - class FieldCommand { - int index; - string oldString; - string newString; - } - - class FieldBase: UIElement { - bool Dragging = false; - EventHandler@ Changed; - string Placeholder; - int MarkPosition = 0; - int CursorPosition = 0; - int MaxLength = 255; - bool DenyNonAscii = false; - - private string text; - private FieldCommand@[] history; - private int historyPos = 0; // index to insert next command - - Vector4 TextColor = Vector4(1.f, 1.f, 1.f, 1.f); - Vector4 DisabledTextColor = Vector4(1.f, 1.f, 1.f, 0.3f); - Vector4 PlaceholderColor = Vector4(1.f, 1.f, 1.f, 0.5f); - Vector4 HighlightColor = Vector4(1.f, 1.f, 1.f, 0.3f); - - Vector2 TextOrigin = Vector2(0.f, 0.f); - float TextScale = 1.f; - - FieldBase(UIManager@ manager) { - super(manager); - IsMouseInteractive = true; - AcceptsFocus = true; - @this.Cursor = Cursor(Manager, manager.Renderer.RegisterImage("Gfx/UI/IBeam.png"), Vector2(16.f, 16.f)); - } - - string Text { - get { return text; } - set { - text = value; - EraseUndoHistory(); - } - } - - private void EraseUndoHistory() { - history.length = 0; - historyPos = 0; - } - - private bool CheckCharType(string s) { - if(DenyNonAscii) { - for(uint i = 0, len = s.length; i < len; i++) { - int c = s[i]; - if((c & 0x80) != 0) { - return false; - } - } - } - return true; - } - - void OnChanged() { - if(Changed !is null) { - Changed(this); - } - } - - int SelectionStart { - get final { return Min(MarkPosition, CursorPosition); } - set { - Select(value, SelectionEnd - value); - } - } - - int SelectionEnd { - get final { - return Max(MarkPosition, CursorPosition); - } - set { - Select(SelectionStart, value - SelectionStart); - } - } - - int SelectionLength { - get final { - return SelectionEnd - SelectionStart; - } - set { - Select(SelectionStart, value); - } - } - - string SelectedText { - get final { - return Text.substr(SelectionStart, SelectionLength); - } - set { - if(!CheckCharType(value)) { - return; - } - FieldCommand cmd; - cmd.oldString = this.SelectedText; - if(cmd.oldString == value) return; // no change - cmd.newString = value; - cmd.index = this.SelectionStart; - RunFieldCommand(cmd, true, true); - } - } - - private void RunFieldCommand(FieldCommand@ cmd, bool autoSelect, bool addHistory) { - text = text.substr(0, cmd.index) + cmd.newString + - text.substr(cmd.index + cmd.oldString.length); - if(autoSelect) - Select(cmd.index, cmd.newString.length); - - if(addHistory) { - history.length = historyPos; - history.insertLast(cmd); - historyPos += 1; - // limit history length - } - - } - - private void UndoFieldCommand(FieldCommand@ cmd, bool autoSelect) { - text = text.substr(0, cmd.index) + cmd.oldString + - text.substr(cmd.index + cmd.newString.length); - if(autoSelect) - Select(cmd.index, cmd.oldString.length); - - } - - private void SetHistoryPos(int index) { - int p = historyPos; - FieldCommand@[]@ h = history; - while(p < index) { - RunFieldCommand(h[p], true, false); - p++; - } - while(p > index) { - p--; - UndoFieldCommand(h[p], true); - } - historyPos = p; - } - - bool Undo() { - if(historyPos == 0) return false; - SetHistoryPos(historyPos - 1); - return true; - } - - bool Redo() { - if(historyPos >= int(history.length)) return false; - SetHistoryPos(historyPos + 1); - return true; - } - - AABB2 TextInputRect { - get { - Vector2 textPos = TextOrigin; - Vector2 siz = this.Size; - string text = Text; - int cursorPos = CursorPosition; - Font@ font = this.Font; - float width = font.Measure(text.substr(0, cursorPos)).x; - float fontHeight = font.Measure("A").y; - return AABB2(textPos.x + width, textPos.y, - siz.x - textPos.x - width, fontHeight); - } - } - - private int PointToCharIndex(float x) { - x -= TextOrigin.x; - if(x < 0.f) return 0; - x /= TextScale; - string text = Text; - int len = text.length; - float lastWidth = 0.f; - Font@ font = this.Font; - // FIXME: use binary search for better performance? - int idx = 0; - for(int i = 1; i <= len; i++) { - int lastIdx = idx; - idx = GetByteIndexForString(text, 1, idx); - float width = font.Measure(text.substr(0, idx)).x; - if(width > x) { - if(x < (lastWidth + width) * 0.5f) { - return lastIdx; - } else { - return idx; - } - } - lastWidth = width; - if(idx >= len) { - return len; - } - } - return len; - } - int PointToCharIndex(Vector2 pt) { - return PointToCharIndex(pt.x); - } - - int ClampCursorPosition(int pos) { - return Clamp(pos, 0, Text.length); - } - - void Select(int start, int length = 0) { - MarkPosition = ClampCursorPosition(start); - CursorPosition = ClampCursorPosition(start + length); - } - - void SelectAll() { - Select(0, Text.length); - } - - void BackSpace() { - if(SelectionLength > 0) { - SelectedText = ""; - } else { - int pos = CursorPosition; - int cIdx = GetCharIndexForString(Text, CursorPosition); - int bIdx = GetByteIndexForString(Text, cIdx - 1); - Select(bIdx, pos - bIdx); - SelectedText = ""; - } - OnChanged(); - } - - void Delete() { - if(SelectionLength > 0) { - SelectedText = ""; - } else if(CursorPosition < int(Text.length)) { - int pos = CursorPosition; - int cIdx = GetCharIndexForString(Text, CursorPosition); - int bIdx = GetByteIndexForString(Text, cIdx + 1); - Select(bIdx, pos - bIdx); - SelectedText = ""; - } - OnChanged(); - } - - void Insert(string text) { - if(!CheckCharType(text)) { - return; - } - string oldText = SelectedText; - SelectedText = text; - - // if text overflows, deny the insertion - if((not FitsInBox(Text)) or (int(Text.length) > MaxLength)) { - SelectedText = oldText; - return; - } - - Select(SelectionEnd); - OnChanged(); - } - - void KeyDown(string key) { - if(key == "BackSpace") { - BackSpace(); - }else if(key == "Delete") { - Delete(); - }else if(key == "Left") { - if(Manager.IsShiftPressed) { - int cIdx = GetCharIndexForString(Text, CursorPosition); - CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx - 1)); - }else { - if(SelectionLength == 0) { - int cIdx = GetCharIndexForString(Text, CursorPosition); - Select(GetByteIndexForString(Text, cIdx - 1)); - } else { - Select(SelectionStart); - } - } - return; - }else if(key == "Right") { - if(Manager.IsShiftPressed) { - int cIdx = GetCharIndexForString(Text, CursorPosition); - CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx + 1)); - }else { - if(SelectionLength == 0) { - int cIdx = GetCharIndexForString(Text, CursorPosition); - Select(GetByteIndexForString(Text, cIdx + 1)); - } else { - Select(SelectionEnd); - } - } - return; - } - if(Manager.IsControlPressed or - Manager.IsMetaPressed /* for OSX; Cmd + [a-z] */) { - if(key == "A") { - SelectAll(); - return; - }else if(key == "V") { - Manager.Paste(PasteClipboardEventHandler(this.Insert)); - }else if(key == "C") { - Manager.Copy(this.SelectedText); - }else if(key == "X") { - Manager.Copy(this.SelectedText); - this.SelectedText = ""; - OnChanged(); - }else if(key == "Z") { - if(Manager.IsShiftPressed){ - if(Redo()) OnChanged(); - }else{ - if(Undo()) OnChanged(); - } - }else if(key == "W") { - if(Redo()) { - OnChanged(); - } - } - } - Manager.ProcessHotKey(key); - } - void KeyUp(string key) { - } - - void KeyPress(string text) { - if(!(Manager.IsControlPressed or - Manager.IsMetaPressed)) { - Insert(text); - } - } - void MouseDown(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - Dragging = true; - if(Manager.IsShiftPressed) { - MouseMove(clientPosition); - } else { - Select(PointToCharIndex(clientPosition)); - } - } - void MouseMove(Vector2 clientPosition) { - if(Dragging) { - CursorPosition = PointToCharIndex(clientPosition); - } - } - void MouseUp(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - Dragging = false; - } - - bool FitsInBox(string text) { - return Font.Measure(text).x * TextScale < Size.x - TextOrigin.x; - } - - void DrawHighlight(float x, float y, float w, float h) { - Renderer@ renderer = Manager.Renderer; - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.DrawImage(img, AABB2(x, y, w, h)); - } - - void DrawBeam(float x, float y, float h) { - Renderer@ renderer = Manager.Renderer; - float pulse = sin(Manager.Time * 5.f); - pulse = abs(pulse); - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, pulse); - - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.DrawImage(img, AABB2(x - 1.f, y, 2, h)); - } - - void DrawEditingLine(float x, float y, float w, float h) { - Renderer@ renderer = Manager.Renderer; - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, .3f); - - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.DrawImage(img, AABB2(x, y + h, w, 2.f)); - } - - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Font@ font = this.Font; - Vector2 textPos = TextOrigin + pos; - string text = Text; - - string composition = this.EditingText; - int editStart = this.TextEditingRangeStart; - int editLen = this.TextEditingRangeLength; - - int markStart = SelectionStart; - int markEnd = SelectionEnd; - - if(composition.length > 0){ - this.SelectedText = ""; - markStart = SelectionStart + editStart; - markEnd = markStart + editLen; - text = text.substr(0, SelectionStart) + composition + text.substr(SelectionStart); - } - - if(text.length == 0){ - if(IsEnabled) { - font.Draw(Placeholder, textPos, TextScale, PlaceholderColor); - } - }else{ - font.Draw(text, textPos, TextScale, IsEnabled ? TextColor : DisabledTextColor); - } - - if(IsFocused){ - float fontHeight = font.Measure("A").y; - - // draw selection - int start = markStart; - int end = markEnd; - if(end == start) { - float x = font.Measure(text.substr(0, start)).x; - DrawBeam(x + textPos.x, textPos.y, fontHeight); - } else { - float x1 = font.Measure(text.substr(0, start)).x; - float x2 = font.Measure(text.substr(0, end)).x; - DrawHighlight(textPos.x + x1, textPos.y, x2 - x1, fontHeight); - } - - // draw composition underline - if(composition.length > 0) { - start = SelectionStart; - end = start + composition.length; - float x1 = font.Measure(text.substr(0, start)).x; - float x2 = font.Measure(text.substr(0, end)).x; - DrawEditingLine(textPos.x + x1, textPos.y, x2 - x1, fontHeight); - } - } - - UIElement::Render(); - } - } - - class Field: FieldBase { - private bool hover; - Field(UIManager@ manager) { - super(manager); - TextOrigin = Vector2(2.f, 2.f); - } - void MouseEnter() { - hover = true; - FieldBase::MouseEnter(); - } - void MouseLeave() { - hover = false; - FieldBase::MouseLeave(); - } - void Render() { - // render background - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, IsFocused ? 0.3f : 0.1f); - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - - if(IsFocused) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - }else if(hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.06f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x, pos.y + size.y - 1.f, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x, pos.y + 1.f, 1.f, size.y - 2.f)); - renderer.DrawImage(img, AABB2(pos.x + size.x - 1.f, pos.y + 1.f, 1.f, size.y - 2.f)); - - FieldBase::Render(); - } - } - - enum ScrollBarOrientation { - Horizontal, - Vertical - } - - class ScrollBarBase: UIElement { - double MinValue = 0.0; - double MaxValue = 100.0; - double Value = 0.0; - double SmallChange = 1.0; - double LargeChange = 20.0; - EventHandler@ Changed; - - ScrollBarBase(UIManager@ manager) { - super(manager); - } - - void ScrollBy(double delta) { - ScrollTo(Value + delta); - } - - void ScrollTo(double val) { - val = Clamp(val, MinValue, MaxValue); - if(val == Value) { - return; - } - Value = val; - OnChanged(); - } - - void OnChanged() { - if(Changed !is null) { - Changed(this); - } - } - - ScrollBarOrientation Orientation { - get { - if(Size.x > Size.y) { - return spades::ui::ScrollBarOrientation::Horizontal; - } else { - return spades::ui::ScrollBarOrientation::Vertical; - } - } - } - - - } - - class ScrollBarTrackBar: UIElement { - private ScrollBar@ scrollBar; - private bool dragging = false; - private double startValue; - private float startCursorPos; - private bool hover = false; - - ScrollBarTrackBar(ScrollBar@ scrollBar) { - super(scrollBar.Manager); - @this.scrollBar = scrollBar; - IsMouseInteractive = true; - } - - private float GetCursorPos(Vector2 pos) { - if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - return pos.x + Position.x; - } else { - return pos.y + Position.y; - } - } - - void MouseDown(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - if(scrollBar.TrackBarMovementRange < 0.0001f) { - // immobile - return; - } - dragging = true; - startValue = scrollBar.Value; - startCursorPos = GetCursorPos(clientPosition); - } - void MouseMove(Vector2 clientPosition) { - if(dragging) { - double val = startValue; - float delta = GetCursorPos(clientPosition) - startCursorPos; - val += delta * (scrollBar.MaxValue - scrollBar.MinValue) / - double(scrollBar.TrackBarMovementRange); - scrollBar.ScrollTo(val); - } - } - void MouseUp(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - dragging = false; - } - void MouseEnter() { - hover = true; - UIElement::MouseEnter(); - } - void MouseLeave() { - hover = false; - UIElement::MouseLeave(); - } - - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - - if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - pos.y += 4.f; size.y -= 8.f; - } else { - pos.x += 4.f; size.x -= 8.f; - } - - if(dragging) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.4f); - } else if (hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - } - } - - class ScrollBarFill: ButtonBase { - private ScrollBarBase@ scrollBar; - private bool up; - - ScrollBarFill(ScrollBarBase@ scrollBar, bool up) { - super(scrollBar.Manager); - @this.scrollBar = scrollBar; - IsMouseInteractive = true; - Repeat = true; - this.up = up; - } - - void PlayMouseEnterSound() { - // suppress - } - - void PlayActivateSound() { - // suppress - } - - void Render() { - // nothing to draw - } - } - - class ScrollBarButton: ButtonBase { - private ScrollBar@ scrollBar; - private bool up; - private Image@ image; - - ScrollBarButton(ScrollBar@ scrollBar, bool up) { - super(scrollBar.Manager); - @this.scrollBar = scrollBar; - IsMouseInteractive = true; - Repeat = true; - this.up = up; - @image = Manager.Renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); - } - - void PlayMouseEnterSound() { - // suppress - } - - void PlayActivateSound() { - // suppress - } - - void Render() { - Renderer@ r = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - pos += size * 0.5f; - float siz = image.Width * 0.5f; - AABB2 srcRect(0.f, 0.f, image.Width, image.Height); - - if(Pressed and Hover) { - r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.6f); - } else if (Hover) { - r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.4f); - } else { - r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); - } - - if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - if(up) { - r.DrawImage(image, - Vector2(pos.x + siz, pos.y - siz), Vector2(pos.x + siz, pos.y + siz), Vector2(pos.x - siz, pos.y - siz), - srcRect); - } else { - r.DrawImage(image, - Vector2(pos.x - siz, pos.y + siz), Vector2(pos.x - siz, pos.y - siz), Vector2(pos.x + siz, pos.y + siz), - srcRect); - } - } else { - if(up) { - r.DrawImage(image, - Vector2(pos.x + siz, pos.y + siz), Vector2(pos.x - siz, pos.y + siz), Vector2(pos.x + siz, pos.y - siz), - srcRect); - } else { - r.DrawImage(image, - Vector2(pos.x - siz, pos.y - siz), Vector2(pos.x + siz, pos.y - siz), Vector2(pos.x - siz, pos.y + siz), - srcRect); - } - } - } - } - - class ScrollBar: ScrollBarBase { - - private ScrollBarTrackBar@ trackBar; - private ScrollBarFill@ fill1; - private ScrollBarFill@ fill2; - private ScrollBarButton@ button1; - private ScrollBarButton@ button2; - - private float ButtonSize = 16.f; - - ScrollBar(UIManager@ manager) { - super(manager); - - @trackBar = ScrollBarTrackBar(this); - AddChild(trackBar); - - @fill1 = ScrollBarFill(this, false); - @fill1.Activated = EventHandler(this.LargeDown); - AddChild(fill1); - @fill2 = ScrollBarFill(this, true); - @fill2.Activated = EventHandler(this.LargeUp); - AddChild(fill2); - - @button1 = ScrollBarButton(this, false); - @button1.Activated = EventHandler(this.SmallDown); - AddChild(button1); - @button2 = ScrollBarButton(this, true); - @button2.Activated = EventHandler(this.SmallUp); - AddChild(button2); - } - - private void LargeDown(UIElement@ e) { - ScrollBy(-LargeChange); - } - private void LargeUp(UIElement@ e) { - ScrollBy(LargeChange); - } - private void SmallDown(UIElement@ e) { - ScrollBy(-SmallChange); - } - private void SmallUp(UIElement@ e) { - ScrollBy(SmallChange); - } - - void OnChanged() { - Layout(); - ScrollBarBase::OnChanged(); - Layout(); - } - - void Layout() { - Vector2 size = Size; - float tPos = TrackBarPosition; - float tLen = TrackBarLength; - if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - button1.Bounds = AABB2(0.f, 0.f, ButtonSize, size.y); - button2.Bounds = AABB2(size.x - ButtonSize, 0.f, ButtonSize, size.y); - fill1.Bounds = AABB2(ButtonSize, 0.f, tPos - ButtonSize, size.y); - fill2.Bounds = AABB2(tPos + tLen, 0.f, size.x - ButtonSize - tPos - tLen, size.y); - trackBar.Bounds = AABB2(tPos, 0.f, tLen, size.y); - } else { - button1.Bounds = AABB2(0.f, 0.f, size.x, ButtonSize); - button2.Bounds = AABB2(0.f, size.y - ButtonSize, size.x, ButtonSize); - fill1.Bounds = AABB2(0.f, ButtonSize, size.x, tPos - ButtonSize); - fill2.Bounds = AABB2(0.f, tPos + tLen, size.x, size.y - ButtonSize - tPos - tLen); - trackBar.Bounds = AABB2(0.f, tPos, size.x, tLen); - } - } - - void OnResized() { - Layout(); - UIElement::OnResized(); - } - - float Length { - get { - if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - return Size.x; - } else { - return Size.y; - } - } - } - - float TrackBarAreaLength { - get { - return Length - ButtonSize - ButtonSize; - } - } - - float TrackBarLength { - get { - return Max(TrackBarAreaLength * - (LargeChange / (MaxValue - MinValue + LargeChange)), 40.f); - } - } - - float TrackBarMovementRange { - get { - return TrackBarAreaLength - TrackBarLength; - } - } - - float TrackBarPosition { - get { - if(MaxValue == MinValue) { - return ButtonSize; - } - return float((Value - MinValue) / (MaxValue - MinValue) * TrackBarMovementRange) + ButtonSize; - } - } - - void Render() { - Layout(); - - ScrollBarBase::Render(); - } - } - - - class SliderKnob: UIElement { - private Slider@ slider; - private bool dragging = false; - private double startValue; - private float startCursorPos; - private bool hover = false; - - SliderKnob(Slider@ slider) { - super(slider.Manager); - @this.slider = slider; - IsMouseInteractive = true; - } - - private float GetCursorPos(Vector2 pos) { - return pos.x + Position.x; - } - - void MouseDown(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - dragging = true; - startValue = slider.Value; - startCursorPos = GetCursorPos(clientPosition); - } - void MouseMove(Vector2 clientPosition) { - if(dragging) { - double val = startValue; - float delta = GetCursorPos(clientPosition) - startCursorPos; - val += delta * (slider.MaxValue - slider.MinValue) / - double(slider.TrackBarMovementRange); - slider.ScrollTo(val); - } - } - void MouseUp(MouseButton button, Vector2 clientPosition) { - if(button != spades::ui::MouseButton::LeftMouseButton) { - return; - } - dragging = false; - } - void MouseEnter() { - hover = true; - UIElement::MouseEnter(); - } - void MouseLeave() { - hover = false; - UIElement::MouseLeave(); - } - - void Render() { - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - - if (hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.5f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); - } - renderer.DrawImage(img, - AABB2(pos.x + size.x * 0.5f - 3.f, pos.y, - 6.f, size.y)); - - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.6f); - renderer.DrawImage(img, - AABB2(pos.x + size.x * 0.5f - 2.f, pos.y + 1.f, - 4.f, size.y - 2.f)); - } - } - - class Slider: ScrollBarBase { - - private SliderKnob@ knob; - private ScrollBarFill@ fill1; - private ScrollBarFill@ fill2; - - Slider(UIManager@ manager) { - super(manager); - - @knob = SliderKnob(this); - AddChild(knob); - - @fill1 = ScrollBarFill(this, false); - @fill1.Activated = EventHandler(this.LargeDown); - AddChild(fill1); - @fill2 = ScrollBarFill(this, true); - @fill2.Activated = EventHandler(this.LargeUp); - AddChild(fill2); - - } - - private void LargeDown(UIElement@ e) { - ScrollBy(-LargeChange); - } - private void LargeUp(UIElement@ e) { - ScrollBy(LargeChange); - }/* - private void SmallDown(UIElement@ e) { - ScrollBy(-SmallChange); - } - private void SmallUp(UIElement@ e) { - ScrollBy(SmallChange); - }*/ - - void OnChanged() { - Layout(); - ScrollBarBase::OnChanged(); - Layout(); - } - - void Layout() { - Vector2 size = Size; - float tPos = TrackBarPosition; - float tLen = TrackBarLength; - fill1.Bounds = AABB2(0.f, 0.f, tPos, size.y); - fill2.Bounds = AABB2(tPos + tLen, 0.f, size.x - tPos - tLen, size.y); - knob.Bounds = AABB2(tPos, 0.f, tLen, size.y); - } - - void OnResized() { - Layout(); - UIElement::OnResized(); - } - - float Length { - get { - if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { - return Size.x; - } else { - return Size.y; - } - } - } - - float TrackBarAreaLength { - get { - return Length; - } - } - - float TrackBarLength { - get { - return 16.f; - } - } - - float TrackBarMovementRange { - get { - return TrackBarAreaLength - TrackBarLength; - } - } - - float TrackBarPosition { - get { - return float((Value - MinValue) / (MaxValue - MinValue) * TrackBarMovementRange); - } - } - - void Render() { - Layout(); - - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); - renderer.DrawImage(img, - AABB2(pos.x, pos.y + size.y * 0.5f - 3.f, - size.x, 6.f)); - - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.2f); - renderer.DrawImage(img, - AABB2(pos.x + 1.f, pos.y + size.y * 0.5f - 2.f, - size.x - 2.f, 4.f)); - - ScrollBarBase::Render(); - } - } - - class ListViewModel { - int NumRows { get { return 0; } } - UIElement@ CreateElement(int row) { return null; } - void RecycleElement(UIElement@ elem) {} - } - - /** Simple virtual stack panel implementation. */ - class ListViewBase: UIElement { - private ScrollBar@ scrollBar; - private ListViewModel@ model; - float RowHeight = 24.f; - float ScrollBarWidth = 16.f; - private UIElementDeque items; - private int loadedStartIndex = 0; - - ListViewBase(UIManager@ manager) { - super(manager); - @scrollBar = ScrollBar(Manager); - scrollBar.Bounds = AABB2(); - AddChild(scrollBar); - IsMouseInteractive = true; - - @scrollBar.Changed = EventHandler(this.OnScrolled); - @model = ListViewModel(); - } - - private void OnScrolled(UIElement@ sender) { - Layout(); - } - - int NumVisibleRows { - get final { - return int(floor(Size.y / RowHeight)); - } - } - - int MaxTopRowIndex { - get final { - return Max(0, model.NumRows - NumVisibleRows); - } - } - - int TopRowIndex { - get final { - int idx = int(floor(scrollBar.Value + 0.5)); - return Clamp(idx, 0, MaxTopRowIndex); - } - } - - void OnResized() { - Layout(); - UIElement::OnResized(); - } - - void Layout() { - scrollBar.MaxValue = double(MaxTopRowIndex); - scrollBar.ScrollTo(scrollBar.Value); // ensures value is in range - scrollBar.LargeChange = double(NumVisibleRows); - - int numRows = model.NumRows; - - // load items - int visibleStart = TopRowIndex; - int visibleEnd = Min(visibleStart + NumVisibleRows, numRows); - int loadedStart = loadedStartIndex; - int loadedEnd = loadedStartIndex + items.Count; - - if(items.Count == 0 or visibleStart >= loadedEnd or visibleEnd <= loadedStart) { - // full reload - UnloadAll(); - for(int i = visibleStart; i < visibleEnd; i++) { - items.PushBack(model.CreateElement(i)); - AddChild(items.Back); - } - loadedStartIndex = visibleStart; - } else { - while(loadedStart < visibleStart) { - RemoveChild(items.Front); - model.RecycleElement(items.Front); - items.PopFront(); - loadedStart++; - } - while(loadedEnd > visibleEnd) { - RemoveChild(items.Back); - model.RecycleElement(items.Back); - items.PopBack(); - loadedEnd--; - } - while(visibleStart < loadedStart) { - loadedStart--; - items.PushFront(model.CreateElement(loadedStart)); - AddChild(items.Front); - } - while(visibleEnd > loadedEnd) { - items.PushBack(model.CreateElement(loadedEnd)); - AddChild(items.Back); - loadedEnd++; - } - loadedStartIndex = loadedStart; - } - - // relayout items - UIElementDeque@ items = this.items; - int count = items.Count; - float y = 0.f; - float w = ItemWidth; - for(int i = 0; i < count; i++){ - items[i].Bounds = AABB2(0.f, y, w, RowHeight); - y += RowHeight; - } - - // move scroll bar - scrollBar.Bounds = AABB2(Size.x - ScrollBarWidth, 0.f, ScrollBarWidth, Size.y); - } - - float ItemWidth { - get { - return Size.x - ScrollBarWidth; - } - } - - void MouseWheel(float delta) { - scrollBar.ScrollBy(delta); - } - - void Reload() { - UnloadAll(); - Layout(); - } - - private void UnloadAll() { - UIElementDeque@ items = this.items; - int count = items.Count; - for(int i = 0; i < count; i++){ - RemoveChild(items[i]); - model.RecycleElement(items[i]); - } - items.Clear(); - } - - ListViewModel@ Model { - get final { return model; } - set { - if(model is value) { - return; - } - UnloadAll(); - @model = value; - Layout(); - } - } - - void ScrollToTop() { - scrollBar.ScrollTo(0.0); - } - - void ScrollToEnd() { - scrollBar.ScrollTo(scrollBar.MaxValue); - } - } - - class TextViewerModel: ListViewModel { - UIManager@ manager; - string[]@ lines = array(); - Vector4[]@ colors = array(); - Font@ font; - float width; - void AddLine(string text, Vector4 color) { - int startPos = 0; - if(font.Measure(text).x <= width) { - lines.insertLast(text); - colors.insertLast(color); - return; - } - - int pos = 0; - int len = int(text.length); - bool charMode = false; - while(startPos < len) { - int nextPos = pos + 1; - if(charMode) { - // skip to the next UTF-8 character boundary - while(nextPos < len && ((text[nextPos] & 0x80) != 0) && - ((text[nextPos] & 0xc0) != 0xc0)) - nextPos++; - } else { - while(nextPos < len && text[nextPos] != 0x20) - nextPos++; - } - if(font.Measure(text.substr(startPos, nextPos - startPos)).x > width) { - if(pos == startPos) { - if(charMode) { - pos = nextPos; - }else{ - charMode = true; - } - continue; - }else{ - lines.insertLast(text.substr(startPos, pos - startPos)); - colors.insertLast(color); - startPos = pos; - while(startPos < len && text[startPos] == 0x20) - startPos++; - pos = startPos; - charMode = false; - continue; - } - }else{ - pos = nextPos; - if(nextPos >= len) { - lines.insertLast(text.substr(startPos, nextPos - startPos)); - colors.insertLast(color); - break; - } - } - } - - } - TextViewerModel(UIManager@ manager, string text, Font@ font, float width) { - @this.manager = manager; - @this.font = font; - this.width = width; - string[]@ lines = text.split("\n"); - for(uint i = 0; i < lines.length; i++) - AddLine(lines[i], Vector4(1.f, 1.f, 1.f, 1.f)); - } - int NumRows { get { return int(lines.length); } } - UIElement@ CreateElement(int row) { - Label i(manager); - i.Text = lines[row]; - i.TextColor = colors[row]; - return i; - } - void RecycleElement(UIElement@ elem) {} - } - - class ListView: ListViewBase { - ListView(UIManager@ manager) { - super(manager); - } - void Render() { - // render background - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.2f); - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.06f); - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x, pos.y + size.y - 1.f, size.x, 1.f)); - renderer.DrawImage(img, AABB2(pos.x, pos.y + 1.f, 1.f, size.y - 2.f)); - renderer.DrawImage(img, AABB2(pos.x + size.x - 1.f, pos.y + 1.f, 1.f, size.y - 2.f)); - - ListViewBase::Render(); - } - } - - class TextViewer: ListViewBase { - private string text; - private TextViewerModel@ textmodel; - - TextViewer(UIManager@ manager) { - super(manager); - } - - /** Sets the displayed text. Ensure TextViewer.Font is not null before setting this proeprty. */ - string Text { - get final { return text; } - set { - text = value; - @textmodel = TextViewerModel(Manager, text, Font, ItemWidth); - @Model = textmodel; - } - } - - void AddLine(string line, bool autoscroll = false, Vector4 color = Vector4(1.f, 1.f, 1.f, 1.f)) { - if(textmodel is null) { - this.Text = ""; - } - if(autoscroll){ - this.Layout(); - if(this.scrollBar.Value < this.scrollBar.MaxValue) { - autoscroll = false; - } - } - textmodel.AddLine(line, color); - if(autoscroll) { - this.Layout(); - this.ScrollToEnd(); - } - } - } - - class SimpleTabStripItem: ButtonBase { - UIElement@ linkedElement; - - SimpleTabStripItem(UIManager@ manager, UIElement@ linkedElement) { - super(manager); - @this.linkedElement = linkedElement; - } - - void MouseDown(MouseButton button, Vector2 clientPosition) { - PlayActivateSound(); - OnActivated(); - } - - void Render() { - this.Toggled = linkedElement.Visible; - - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Vector4 textColor(0.9f, 0.9f, 0.9f, 1.0f); - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - if(Toggled) { - renderer.ColorNP = Vector4(0.9f, 0.9f, 0.9f, 1.0f); - textColor = Vector4(0.0f, 0.0f, 0.0f, 1.0f); - } else if(Hover) { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); - } else { - renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.0f); - } - renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); - - Vector2 txtSize = Font.Measure(Caption); - Font.Draw(Caption, pos + (size - txtSize) * 0.5f, 1.f, textColor); - } - } - - class SimpleTabStrip: UIElement { - private float nextX = 0.f; - - EventHandler@ Changed; - - SimpleTabStrip(UIManager@ manager) { - super(manager); - } - - private void OnChanged() { - if(Changed !is null) { - Changed(this); - } - } - - private void OnItemActivated(UIElement@ sender) { - SimpleTabStripItem@ item = cast(sender); - UIElement@ linked = item.linkedElement; - UIElement@[]@ children = this.GetChildren(); - for(uint i = 0, count = children.length; i < count; i++) { - SimpleTabStripItem@ otherItem = cast(children[i]); - otherItem.linkedElement.Visible = (otherItem.linkedElement is linked); - } - OnChanged(); - } - - void AddItem(string title, UIElement@ linkedElement) { - SimpleTabStripItem item(this.Manager, linkedElement); - item.Caption = title; - float w = this.Font.Measure(title).x + 18.f; - item.Bounds = AABB2(nextX, 0.f, w, 24.f); - nextX += w + 4.f; - - @item.Activated = EventHandler(this.OnItemActivated); - - this.AddChild(item); - - } - - void Render() { - UIElement::Render(); - - Renderer@ renderer = Manager.Renderer; - Vector2 pos = ScreenPosition; - Vector2 size = Size; - Image@ img = renderer.RegisterImage("Gfx/White.tga"); - renderer.ColorNP = Vector4(0.9f, 0.9f, 0.9f, 1.0f); - renderer.DrawImage(img, AABB2(pos.x, pos.y + 24.f, size.x, 1.f)); - } - - } - - } -} diff --git a/Resources/Scripts/Gui/UIFramework/Button.as b/Resources/Scripts/Gui/UIFramework/Button.as new file mode 100644 index 00000000..0b59ddf9 --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/Button.as @@ -0,0 +1,368 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "UIFramework.as" + +namespace spades { + namespace ui { + + class ButtonBase: UIElement { + bool Pressed = false; + bool Hover = false; + bool Toggled = false; + + bool Toggle = false; + bool Repeat = false; + bool ActivateOnMouseDown = false; + + EventHandler@ Activated; + EventHandler@ DoubleClicked; + EventHandler@ RightClicked; + string Caption; + string ActivateHotKey; + + private Timer@ repeatTimer; + + // for double click detection + private float lastActivate = -1.f; + private Vector2 lastActivatePosition = Vector2(); + + ButtonBase(UIManager@ manager) { + super(manager); + IsMouseInteractive = true; + @repeatTimer = Timer(Manager); + @repeatTimer.Tick = TimerTickEventHandler(this.RepeatTimerFired); + } + + void PlayMouseEnterSound() { + Manager.PlaySound("Sounds/Feedback/Limbo/Hover.opus"); + } + + void PlayActivateSound() { + Manager.PlaySound("Sounds/Feedback/Limbo/Select.opus"); + } + + void OnActivated() { + if(Activated !is null) { + Activated(this); + } + } + + void OnDoubleClicked() { + if(DoubleClicked !is null) { + DoubleClicked(this); + } + } + + void OnRightClicked() { + if(RightClicked !is null) { + RightClicked(this); + } + } + + private void RepeatTimerFired(Timer@ timer) { + OnActivated(); + timer.Interval = 0.1f; + } + + void MouseDown(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton && + button != spades::ui::MouseButton::RightMouseButton) { + return; + } + + PlayActivateSound(); + if (button == spades::ui::MouseButton::RightMouseButton) { + OnRightClicked(); + return; + } + + Pressed = true; + Hover = true; + + if(Repeat or ActivateOnMouseDown) { + OnActivated(); + if(Repeat) { + repeatTimer.Interval = 0.3f; + repeatTimer.Start(); + } + } + } + void MouseMove(Vector2 clientPosition) { + if(Pressed) { + bool newHover = AABB2(Vector2(0.f, 0.f), Size).Contains(clientPosition); + if(newHover != Hover) { + if(Repeat) { + if(newHover) { + OnActivated(); + repeatTimer.Interval = 0.3f; + repeatTimer.Start(); + } else { + repeatTimer.Stop(); + } + } + Hover = newHover; + } + } + } + void MouseUp(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton && + button != spades::ui::MouseButton::RightMouseButton) { + return; + } + if(Pressed) { + Pressed = false; + if(Hover and not (Repeat or ActivateOnMouseDown)) { + if(Toggle) { + Toggled = not Toggled; + } + OnActivated(); + if (Manager.Time < lastActivate + 0.35 && + (clientPosition - lastActivatePosition).ManhattanLength < 10.0f) { + OnDoubleClicked(); + } + lastActivate = Manager.Time; + lastActivatePosition = clientPosition; + } + + if(Repeat and Hover){ + repeatTimer.Stop(); + } + } + } + void MouseEnter() { + Hover = true; + if(not Pressed) { + PlayMouseEnterSound(); + } + UIElement::MouseEnter(); + } + void MouseLeave() { + Hover = false; + UIElement::MouseLeave(); + } + + void KeyDown(string key) { + if(key == " ") { + OnActivated(); + } + UIElement::KeyDown(key); + } + void KeyUp(string key) { + UIElement::KeyUp(key); + } + + void HotKey(string key) { + if(key == ActivateHotKey) { + OnActivated(); + } + } + + } + + class SimpleButton: spades::ui::Button { + SimpleButton(spades::ui::UIManager@ manager){ + super(manager); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + if((Pressed && Hover) || Toggled) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + if((Pressed && Hover) || Toggled) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, 1.f, size.y)); + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x+size.x-1.f, pos.y, 1.f, size.y)); + renderer.DrawImage(img, AABB2(pos.x, pos.y+size.y-1.f, size.x, 1.f)); + Vector2 txtSize = Font.Measure(Caption); + float margin = 4.f; + Font.DrawShadow(Caption, pos + Vector2(margin, margin) + + (size - txtSize - Vector2(margin * 2.f, margin * 2.f)) * Alignment, + 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.4f)); + } + } + + + class CheckBox: spades::ui::Button { + CheckBox(spades::ui::UIManager@ manager){ + super(manager); + this.Toggle = true; + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + if(Pressed && Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.00f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + Vector2 txtSize = Font.Measure(Caption); + Font.DrawShadow(Caption, pos + (size - txtSize) * Vector2(0.f, 0.5f) + Vector2(16.f, 0.f), + 1.f, Vector4(1,1,1,1), Vector4(0,0,0,0.2f)); + + @img = renderer.RegisterImage("Gfx/UI/CheckBox.png"); + + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, Toggled ? .9f : 0.6f); + renderer.DrawImage(img, AABB2(pos.x, pos.y + (size.y - 16.f) * 0.5f, 16.f, 16.f), + AABB2(Toggled ? 16.f : 0.f, 0.f, 16.f, 16.f)); + + } + } + + + class RadioButton: spades::ui::Button { + string GroupName; + + RadioButton(spades::ui::UIManager@ manager){ + super(manager); + this.Toggle = true; + } + void Check() { + this.Toggled = true; + + // uncheck others + if(GroupName.length > 0) { + UIElement@[]@ children = this.Parent.GetChildren(); + for(uint i = 0, count = children.length; i < children.length; i++) { + RadioButton@ btn = cast(children[i]); + if(btn is this) continue; + if(btn !is null) { + if(GroupName == btn.GroupName) { + btn.Toggled = false; + } + } + } + } + } + void OnActivated() { + Check(); + + Button::OnActivated(); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + if(!this.Enable) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); + } else if((Pressed && Hover) || Toggled) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.12f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + if(!this.Enable) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); + } if((Pressed && Hover) || Toggled) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.07f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.03f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, 1.f, size.y)); + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x+size.x-1.f, pos.y, 1.f, size.y)); + renderer.DrawImage(img, AABB2(pos.x, pos.y+size.y-1.f, size.x, 1.f)); + Vector2 txtSize = Font.Measure(Caption); + Font.DrawShadow(Caption, pos + (size - txtSize) * 0.5f + Vector2(8.f, 0.f), 1.f, + Vector4(1,1,1,this.Enable ? 1.f : 0.4f), Vector4(0,0,0,0.4f)); + + if(Toggled) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.6f); + renderer.DrawImage(img, AABB2(pos.x + 4.f, pos.y + (size.y - 8.f) * 0.5f, 8.f, 8.f)); + } + + + } + } + + class Button: ButtonBase { + private Image@ image; + Vector2 Alignment = Vector2(0.5f, 0.5f); + + Button(UIManager@ manager) { + super(manager); + + Renderer@ renderer = Manager.Renderer; + @image = renderer.RegisterImage("Gfx/UI/Button.png"); + } + + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + + Vector4 color = Vector4(0.2f, 0.2f, 0.2f, 0.5f); + if(Toggled or (Pressed and Hover)) { + color = Vector4(0.7f, 0.7f, 0.7f, 0.9f); + }else if(Hover) { + color = Vector4(0.4f, 0.4f, 0.4f, 0.7f); + } + if(!IsEnabled) { + color.w *= 0.5f; + } + renderer.ColorNP = color; + + DrawSliceImage(renderer, image, pos.x, pos.y, size.x, size.y, 12.f); + + Font@ font = this.Font; + string text = this.Caption; + Vector2 txtSize = font.Measure(text); + Vector2 txtPos; + pos += Vector2(8.f, 8.f); + size -= Vector2(16.f, 16.f); + txtPos = pos + (size - txtSize) * Alignment; + + if(IsEnabled){ + font.DrawShadow(text, txtPos, 1.f, + Vector4(1.f, 1.f, 1.f, 1.f), Vector4(0.f, 0.f, 0.f, 0.4f)); + }else{ + font.DrawShadow(text, txtPos, 1.f, + Vector4(1.f, 1.f, 1.f, 0.5f), Vector4(0.f, 0.f, 0.f, 0.1f)); + } + } + + } + + } +} diff --git a/Resources/Scripts/Gui/DropDownList.as b/Resources/Scripts/Gui/UIFramework/DropDownList.as similarity index 100% rename from Resources/Scripts/Gui/DropDownList.as rename to Resources/Scripts/Gui/UIFramework/DropDownList.as diff --git a/Resources/Scripts/Gui/UIFramework/Field.as b/Resources/Scripts/Gui/UIFramework/Field.as new file mode 100644 index 00000000..c1ce90fa --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/Field.as @@ -0,0 +1,504 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "Label.as" + +namespace spades { + namespace ui { + + class FieldCommand { + int index; + string oldString; + string newString; + } + + class FieldBase: UIElement { + bool Dragging = false; + EventHandler@ Changed; + string Placeholder; + int MarkPosition = 0; + int CursorPosition = 0; + int MaxLength = 255; + bool DenyNonAscii = false; + + private string text; + private FieldCommand@[] history; + private int historyPos = 0; // index to insert next command + + Vector4 TextColor = Vector4(1.f, 1.f, 1.f, 1.f); + Vector4 DisabledTextColor = Vector4(1.f, 1.f, 1.f, 0.3f); + Vector4 PlaceholderColor = Vector4(1.f, 1.f, 1.f, 0.5f); + Vector4 HighlightColor = Vector4(1.f, 1.f, 1.f, 0.3f); + + Vector2 TextOrigin = Vector2(0.f, 0.f); + float TextScale = 1.f; + + FieldBase(UIManager@ manager) { + super(manager); + IsMouseInteractive = true; + AcceptsFocus = true; + @this.Cursor = Cursor(Manager, manager.Renderer.RegisterImage("Gfx/UI/IBeam.png"), Vector2(16.f, 16.f)); + } + + string Text { + get { return text; } + set { + text = value; + EraseUndoHistory(); + } + } + + private void EraseUndoHistory() { + history.length = 0; + historyPos = 0; + } + + private bool CheckCharType(string s) { + if(DenyNonAscii) { + for(uint i = 0, len = s.length; i < len; i++) { + int c = s[i]; + if((c & 0x80) != 0) { + return false; + } + } + } + return true; + } + + void OnChanged() { + if(Changed !is null) { + Changed(this); + } + } + + int SelectionStart { + get final { return Min(MarkPosition, CursorPosition); } + set { + Select(value, SelectionEnd - value); + } + } + + int SelectionEnd { + get final { + return Max(MarkPosition, CursorPosition); + } + set { + Select(SelectionStart, value - SelectionStart); + } + } + + int SelectionLength { + get final { + return SelectionEnd - SelectionStart; + } + set { + Select(SelectionStart, value); + } + } + + string SelectedText { + get final { + return Text.substr(SelectionStart, SelectionLength); + } + set { + if(!CheckCharType(value)) { + return; + } + FieldCommand cmd; + cmd.oldString = this.SelectedText; + if(cmd.oldString == value) return; // no change + cmd.newString = value; + cmd.index = this.SelectionStart; + RunFieldCommand(cmd, true, true); + } + } + + private void RunFieldCommand(FieldCommand@ cmd, bool autoSelect, bool addHistory) { + text = text.substr(0, cmd.index) + cmd.newString + + text.substr(cmd.index + cmd.oldString.length); + if(autoSelect) + Select(cmd.index, cmd.newString.length); + + if(addHistory) { + history.length = historyPos; + history.insertLast(cmd); + historyPos += 1; + // limit history length + } + + } + + private void UndoFieldCommand(FieldCommand@ cmd, bool autoSelect) { + text = text.substr(0, cmd.index) + cmd.oldString + + text.substr(cmd.index + cmd.newString.length); + if(autoSelect) + Select(cmd.index, cmd.oldString.length); + + } + + private void SetHistoryPos(int index) { + int p = historyPos; + FieldCommand@[]@ h = history; + while(p < index) { + RunFieldCommand(h[p], true, false); + p++; + } + while(p > index) { + p--; + UndoFieldCommand(h[p], true); + } + historyPos = p; + } + + bool Undo() { + if(historyPos == 0) return false; + SetHistoryPos(historyPos - 1); + return true; + } + + bool Redo() { + if(historyPos >= int(history.length)) return false; + SetHistoryPos(historyPos + 1); + return true; + } + + AABB2 TextInputRect { + get { + Vector2 textPos = TextOrigin; + Vector2 siz = this.Size; + string text = Text; + int cursorPos = CursorPosition; + Font@ font = this.Font; + float width = font.Measure(text.substr(0, cursorPos)).x; + float fontHeight = font.Measure("A").y; + return AABB2(textPos.x + width, textPos.y, + siz.x - textPos.x - width, fontHeight); + } + } + + private int PointToCharIndex(float x) { + x -= TextOrigin.x; + if(x < 0.f) return 0; + x /= TextScale; + string text = Text; + int len = text.length; + float lastWidth = 0.f; + Font@ font = this.Font; + // FIXME: use binary search for better performance? + int idx = 0; + for(int i = 1; i <= len; i++) { + int lastIdx = idx; + idx = GetByteIndexForString(text, 1, idx); + float width = font.Measure(text.substr(0, idx)).x; + if(width > x) { + if(x < (lastWidth + width) * 0.5f) { + return lastIdx; + } else { + return idx; + } + } + lastWidth = width; + if(idx >= len) { + return len; + } + } + return len; + } + int PointToCharIndex(Vector2 pt) { + return PointToCharIndex(pt.x); + } + + int ClampCursorPosition(int pos) { + return Clamp(pos, 0, Text.length); + } + + void Select(int start, int length = 0) { + MarkPosition = ClampCursorPosition(start); + CursorPosition = ClampCursorPosition(start + length); + } + + void SelectAll() { + Select(0, Text.length); + } + + void BackSpace() { + if(SelectionLength > 0) { + SelectedText = ""; + } else { + int pos = CursorPosition; + int cIdx = GetCharIndexForString(Text, CursorPosition); + int bIdx = GetByteIndexForString(Text, cIdx - 1); + Select(bIdx, pos - bIdx); + SelectedText = ""; + } + OnChanged(); + } + + void Delete() { + if(SelectionLength > 0) { + SelectedText = ""; + } else if(CursorPosition < int(Text.length)) { + int pos = CursorPosition; + int cIdx = GetCharIndexForString(Text, CursorPosition); + int bIdx = GetByteIndexForString(Text, cIdx + 1); + Select(bIdx, pos - bIdx); + SelectedText = ""; + } + OnChanged(); + } + + void Insert(string text) { + if(!CheckCharType(text)) { + return; + } + string oldText = SelectedText; + SelectedText = text; + + // if text overflows, deny the insertion + if((not FitsInBox(Text)) or (int(Text.length) > MaxLength)) { + SelectedText = oldText; + return; + } + + Select(SelectionEnd); + OnChanged(); + } + + void KeyDown(string key) { + if(key == "BackSpace") { + BackSpace(); + }else if(key == "Delete") { + Delete(); + }else if(key == "Left") { + if(Manager.IsShiftPressed) { + int cIdx = GetCharIndexForString(Text, CursorPosition); + CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx - 1)); + }else { + if(SelectionLength == 0) { + int cIdx = GetCharIndexForString(Text, CursorPosition); + Select(GetByteIndexForString(Text, cIdx - 1)); + } else { + Select(SelectionStart); + } + } + return; + }else if(key == "Right") { + if(Manager.IsShiftPressed) { + int cIdx = GetCharIndexForString(Text, CursorPosition); + CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx + 1)); + }else { + if(SelectionLength == 0) { + int cIdx = GetCharIndexForString(Text, CursorPosition); + Select(GetByteIndexForString(Text, cIdx + 1)); + } else { + Select(SelectionEnd); + } + } + return; + } + if(Manager.IsControlPressed or + Manager.IsMetaPressed /* for OSX; Cmd + [a-z] */) { + if(key == "A") { + SelectAll(); + return; + }else if(key == "V") { + Manager.Paste(PasteClipboardEventHandler(this.Insert)); + }else if(key == "C") { + Manager.Copy(this.SelectedText); + }else if(key == "X") { + Manager.Copy(this.SelectedText); + this.SelectedText = ""; + OnChanged(); + }else if(key == "Z") { + if(Manager.IsShiftPressed){ + if(Redo()) OnChanged(); + }else{ + if(Undo()) OnChanged(); + } + }else if(key == "W") { + if(Redo()) { + OnChanged(); + } + } + } + Manager.ProcessHotKey(key); + } + void KeyUp(string key) { + } + + void KeyPress(string text) { + if(!(Manager.IsControlPressed or + Manager.IsMetaPressed)) { + Insert(text); + } + } + void MouseDown(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + Dragging = true; + if(Manager.IsShiftPressed) { + MouseMove(clientPosition); + } else { + Select(PointToCharIndex(clientPosition)); + } + } + void MouseMove(Vector2 clientPosition) { + if(Dragging) { + CursorPosition = PointToCharIndex(clientPosition); + } + } + void MouseUp(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + Dragging = false; + } + + bool FitsInBox(string text) { + return Font.Measure(text).x * TextScale < Size.x - TextOrigin.x; + } + + void DrawHighlight(float x, float y, float w, float h) { + Renderer@ renderer = Manager.Renderer; + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.DrawImage(img, AABB2(x, y, w, h)); + } + + void DrawBeam(float x, float y, float h) { + Renderer@ renderer = Manager.Renderer; + float pulse = sin(Manager.Time * 5.f); + pulse = abs(pulse); + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, pulse); + + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.DrawImage(img, AABB2(x - 1.f, y, 2, h)); + } + + void DrawEditingLine(float x, float y, float w, float h) { + Renderer@ renderer = Manager.Renderer; + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, .3f); + + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.DrawImage(img, AABB2(x, y + h, w, 2.f)); + } + + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Font@ font = this.Font; + Vector2 textPos = TextOrigin + pos; + string text = Text; + + string composition = this.EditingText; + int editStart = this.TextEditingRangeStart; + int editLen = this.TextEditingRangeLength; + + int markStart = SelectionStart; + int markEnd = SelectionEnd; + + if(composition.length > 0){ + this.SelectedText = ""; + markStart = SelectionStart + editStart; + markEnd = markStart + editLen; + text = text.substr(0, SelectionStart) + composition + text.substr(SelectionStart); + } + + if(text.length == 0){ + if(IsEnabled) { + font.Draw(Placeholder, textPos, TextScale, PlaceholderColor); + } + }else{ + font.Draw(text, textPos, TextScale, IsEnabled ? TextColor : DisabledTextColor); + } + + if(IsFocused){ + float fontHeight = font.Measure("A").y; + + // draw selection + int start = markStart; + int end = markEnd; + if(end == start) { + float x = font.Measure(text.substr(0, start)).x; + DrawBeam(x + textPos.x, textPos.y, fontHeight); + } else { + float x1 = font.Measure(text.substr(0, start)).x; + float x2 = font.Measure(text.substr(0, end)).x; + DrawHighlight(textPos.x + x1, textPos.y, x2 - x1, fontHeight); + } + + // draw composition underline + if(composition.length > 0) { + start = SelectionStart; + end = start + composition.length; + float x1 = font.Measure(text.substr(0, start)).x; + float x2 = font.Measure(text.substr(0, end)).x; + DrawEditingLine(textPos.x + x1, textPos.y, x2 - x1, fontHeight); + } + } + + UIElement::Render(); + } + } + + class Field: FieldBase { + private bool hover; + Field(UIManager@ manager) { + super(manager); + TextOrigin = Vector2(2.f, 2.f); + } + void MouseEnter() { + hover = true; + FieldBase::MouseEnter(); + } + void MouseLeave() { + hover = false; + FieldBase::MouseLeave(); + } + void Render() { + // render background + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, IsFocused ? 0.3f : 0.1f); + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + + if(IsFocused) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + }else if(hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.06f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x, pos.y + size.y - 1.f, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x, pos.y + 1.f, 1.f, size.y - 2.f)); + renderer.DrawImage(img, AABB2(pos.x + size.x - 1.f, pos.y + 1.f, 1.f, size.y - 2.f)); + + FieldBase::Render(); + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/Label.as b/Resources/Scripts/Gui/UIFramework/Label.as new file mode 100644 index 00000000..badd3a3f --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/Label.as @@ -0,0 +1,60 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "UIFramework.as" + +namespace spades { + namespace ui { + + class Label: UIElement { + string Text; + Vector4 BackgroundColor = Vector4(0, 0, 0, 0); + Vector4 TextColor = Vector4(1, 1, 1, 1); + Vector2 Alignment = Vector2(0.f, 0.0f); + float TextScale = 1.f; + + Label(UIManager@ manager) { + super(manager); + } + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + + if(BackgroundColor.w > 0.f) { + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.ColorNP = BackgroundColor; + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + } + + if(Text.length > 0) { + Font@ font = this.Font; + string text = this.Text; + Vector2 txtSize = font.Measure(text) * TextScale; + Vector2 txtPos; + txtPos = pos + (size - txtSize) * Alignment; + + font.Draw(text, txtPos, TextScale, TextColor); + } + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/ListView.as b/Resources/Scripts/Gui/UIFramework/ListView.as new file mode 100644 index 00000000..3193af1f --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/ListView.as @@ -0,0 +1,212 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "Label.as" + +namespace spades { + namespace ui { + + class ListViewModel { + int NumRows { get { return 0; } } + UIElement@ CreateElement(int row) { return null; } + void RecycleElement(UIElement@ elem) {} + } + + /** Simple virtual stack panel implementation. */ + class ListViewBase: UIElement { + private ScrollBar@ scrollBar; + private ListViewModel@ model; + float RowHeight = 24.f; + float ScrollBarWidth = 16.f; + private UIElementDeque items; + private int loadedStartIndex = 0; + + ListViewBase(UIManager@ manager) { + super(manager); + @scrollBar = ScrollBar(Manager); + scrollBar.Bounds = AABB2(); + AddChild(scrollBar); + IsMouseInteractive = true; + + @scrollBar.Changed = EventHandler(this.OnScrolled); + @model = ListViewModel(); + } + + private void OnScrolled(UIElement@ sender) { + Layout(); + } + + int NumVisibleRows { + get final { + return int(floor(Size.y / RowHeight)); + } + } + + int MaxTopRowIndex { + get final { + return Max(0, model.NumRows - NumVisibleRows); + } + } + + int TopRowIndex { + get final { + int idx = int(floor(scrollBar.Value + 0.5)); + return Clamp(idx, 0, MaxTopRowIndex); + } + } + + void OnResized() { + Layout(); + UIElement::OnResized(); + } + + void Layout() { + scrollBar.MaxValue = double(MaxTopRowIndex); + scrollBar.ScrollTo(scrollBar.Value); // ensures value is in range + scrollBar.LargeChange = double(NumVisibleRows); + + int numRows = model.NumRows; + + // load items + int visibleStart = TopRowIndex; + int visibleEnd = Min(visibleStart + NumVisibleRows, numRows); + int loadedStart = loadedStartIndex; + int loadedEnd = loadedStartIndex + items.Count; + + if(items.Count == 0 or visibleStart >= loadedEnd or visibleEnd <= loadedStart) { + // full reload + UnloadAll(); + for(int i = visibleStart; i < visibleEnd; i++) { + items.PushBack(model.CreateElement(i)); + AddChild(items.Back); + } + loadedStartIndex = visibleStart; + } else { + while(loadedStart < visibleStart) { + RemoveChild(items.Front); + model.RecycleElement(items.Front); + items.PopFront(); + loadedStart++; + } + while(loadedEnd > visibleEnd) { + RemoveChild(items.Back); + model.RecycleElement(items.Back); + items.PopBack(); + loadedEnd--; + } + while(visibleStart < loadedStart) { + loadedStart--; + items.PushFront(model.CreateElement(loadedStart)); + AddChild(items.Front); + } + while(visibleEnd > loadedEnd) { + items.PushBack(model.CreateElement(loadedEnd)); + AddChild(items.Back); + loadedEnd++; + } + loadedStartIndex = loadedStart; + } + + // relayout items + UIElementDeque@ items = this.items; + int count = items.Count; + float y = 0.f; + float w = ItemWidth; + for(int i = 0; i < count; i++){ + items[i].Bounds = AABB2(0.f, y, w, RowHeight); + y += RowHeight; + } + + // move scroll bar + scrollBar.Bounds = AABB2(Size.x - ScrollBarWidth, 0.f, ScrollBarWidth, Size.y); + } + + float ItemWidth { + get { + return Size.x - ScrollBarWidth; + } + } + + void MouseWheel(float delta) { + scrollBar.ScrollBy(delta); + } + + void Reload() { + UnloadAll(); + Layout(); + } + + private void UnloadAll() { + UIElementDeque@ items = this.items; + int count = items.Count; + for(int i = 0; i < count; i++){ + RemoveChild(items[i]); + model.RecycleElement(items[i]); + } + items.Clear(); + } + + ListViewModel@ Model { + get final { return model; } + set { + if(model is value) { + return; + } + UnloadAll(); + @model = value; + Layout(); + } + } + + void ScrollToTop() { + scrollBar.ScrollTo(0.0); + } + + void ScrollToEnd() { + scrollBar.ScrollTo(scrollBar.MaxValue); + } + } + + + class ListView: ListViewBase { + ListView(UIManager@ manager) { + super(manager); + } + void Render() { + // render background + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.2f); + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.06f); + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x, pos.y + size.y - 1.f, size.x, 1.f)); + renderer.DrawImage(img, AABB2(pos.x, pos.y + 1.f, 1.f, size.y - 2.f)); + renderer.DrawImage(img, AABB2(pos.x + size.x - 1.f, pos.y + 1.f, 1.f, size.y - 2.f)); + + ListViewBase::Render(); + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/ScrollBar.as b/Resources/Scripts/Gui/UIFramework/ScrollBar.as new file mode 100644 index 00000000..e02ae8a7 --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/ScrollBar.as @@ -0,0 +1,362 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "UIFramework.as" + +namespace spades { + namespace ui { + + enum ScrollBarOrientation { + Horizontal, + Vertical + } + + class ScrollBarBase: UIElement { + double MinValue = 0.0; + double MaxValue = 100.0; + double Value = 0.0; + double SmallChange = 1.0; + double LargeChange = 20.0; + EventHandler@ Changed; + + ScrollBarBase(UIManager@ manager) { + super(manager); + } + + void ScrollBy(double delta) { + ScrollTo(Value + delta); + } + + void ScrollTo(double val) { + val = Clamp(val, MinValue, MaxValue); + if(val == Value) { + return; + } + Value = val; + OnChanged(); + } + + void OnChanged() { + if(Changed !is null) { + Changed(this); + } + } + + ScrollBarOrientation Orientation { + get { + if(Size.x > Size.y) { + return spades::ui::ScrollBarOrientation::Horizontal; + } else { + return spades::ui::ScrollBarOrientation::Vertical; + } + } + } + + + } + + class ScrollBarTrackBar: UIElement { + private ScrollBar@ scrollBar; + private bool dragging = false; + private double startValue; + private float startCursorPos; + private bool hover = false; + + ScrollBarTrackBar(ScrollBar@ scrollBar) { + super(scrollBar.Manager); + @this.scrollBar = scrollBar; + IsMouseInteractive = true; + } + + private float GetCursorPos(Vector2 pos) { + if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + return pos.x + Position.x; + } else { + return pos.y + Position.y; + } + } + + void MouseDown(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + if(scrollBar.TrackBarMovementRange < 0.0001f) { + // immobile + return; + } + dragging = true; + startValue = scrollBar.Value; + startCursorPos = GetCursorPos(clientPosition); + } + void MouseMove(Vector2 clientPosition) { + if(dragging) { + double val = startValue; + float delta = GetCursorPos(clientPosition) - startCursorPos; + val += delta * (scrollBar.MaxValue - scrollBar.MinValue) / + double(scrollBar.TrackBarMovementRange); + scrollBar.ScrollTo(val); + } + } + void MouseUp(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + dragging = false; + } + void MouseEnter() { + hover = true; + UIElement::MouseEnter(); + } + void MouseLeave() { + hover = false; + UIElement::MouseLeave(); + } + + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + + if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + pos.y += 4.f; size.y -= 8.f; + } else { + pos.x += 4.f; size.x -= 8.f; + } + + if(dragging) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.4f); + } else if (hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + } + } + + class ScrollBarFill: ButtonBase { + private ScrollBarBase@ scrollBar; + private bool up; + + ScrollBarFill(ScrollBarBase@ scrollBar, bool up) { + super(scrollBar.Manager); + @this.scrollBar = scrollBar; + IsMouseInteractive = true; + Repeat = true; + this.up = up; + } + + void PlayMouseEnterSound() { + // suppress + } + + void PlayActivateSound() { + // suppress + } + + void Render() { + // nothing to draw + } + } + + class ScrollBarButton: ButtonBase { + private ScrollBar@ scrollBar; + private bool up; + private Image@ image; + + ScrollBarButton(ScrollBar@ scrollBar, bool up) { + super(scrollBar.Manager); + @this.scrollBar = scrollBar; + IsMouseInteractive = true; + Repeat = true; + this.up = up; + @image = Manager.Renderer.RegisterImage("Gfx/UI/ScrollArrow.png"); + } + + void PlayMouseEnterSound() { + // suppress + } + + void PlayActivateSound() { + // suppress + } + + void Render() { + Renderer@ r = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + pos += size * 0.5f; + float siz = image.Width * 0.5f; + AABB2 srcRect(0.f, 0.f, image.Width, image.Height); + + if(Pressed and Hover) { + r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.6f); + } else if (Hover) { + r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.4f); + } else { + r.ColorNP = Vector4(1.f, 1.f, 1.f, 0.2f); + } + + if(scrollBar.Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + if(up) { + r.DrawImage(image, + Vector2(pos.x + siz, pos.y - siz), Vector2(pos.x + siz, pos.y + siz), Vector2(pos.x - siz, pos.y - siz), + srcRect); + } else { + r.DrawImage(image, + Vector2(pos.x - siz, pos.y + siz), Vector2(pos.x - siz, pos.y - siz), Vector2(pos.x + siz, pos.y + siz), + srcRect); + } + } else { + if(up) { + r.DrawImage(image, + Vector2(pos.x + siz, pos.y + siz), Vector2(pos.x - siz, pos.y + siz), Vector2(pos.x + siz, pos.y - siz), + srcRect); + } else { + r.DrawImage(image, + Vector2(pos.x - siz, pos.y - siz), Vector2(pos.x + siz, pos.y - siz), Vector2(pos.x - siz, pos.y + siz), + srcRect); + } + } + } + } + + class ScrollBar: ScrollBarBase { + + private ScrollBarTrackBar@ trackBar; + private ScrollBarFill@ fill1; + private ScrollBarFill@ fill2; + private ScrollBarButton@ button1; + private ScrollBarButton@ button2; + + private float ButtonSize = 16.f; + + ScrollBar(UIManager@ manager) { + super(manager); + + @trackBar = ScrollBarTrackBar(this); + AddChild(trackBar); + + @fill1 = ScrollBarFill(this, false); + @fill1.Activated = EventHandler(this.LargeDown); + AddChild(fill1); + @fill2 = ScrollBarFill(this, true); + @fill2.Activated = EventHandler(this.LargeUp); + AddChild(fill2); + + @button1 = ScrollBarButton(this, false); + @button1.Activated = EventHandler(this.SmallDown); + AddChild(button1); + @button2 = ScrollBarButton(this, true); + @button2.Activated = EventHandler(this.SmallUp); + AddChild(button2); + } + + private void LargeDown(UIElement@ e) { + ScrollBy(-LargeChange); + } + private void LargeUp(UIElement@ e) { + ScrollBy(LargeChange); + } + private void SmallDown(UIElement@ e) { + ScrollBy(-SmallChange); + } + private void SmallUp(UIElement@ e) { + ScrollBy(SmallChange); + } + + void OnChanged() { + Layout(); + ScrollBarBase::OnChanged(); + Layout(); + } + + void Layout() { + Vector2 size = Size; + float tPos = TrackBarPosition; + float tLen = TrackBarLength; + if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + button1.Bounds = AABB2(0.f, 0.f, ButtonSize, size.y); + button2.Bounds = AABB2(size.x - ButtonSize, 0.f, ButtonSize, size.y); + fill1.Bounds = AABB2(ButtonSize, 0.f, tPos - ButtonSize, size.y); + fill2.Bounds = AABB2(tPos + tLen, 0.f, size.x - ButtonSize - tPos - tLen, size.y); + trackBar.Bounds = AABB2(tPos, 0.f, tLen, size.y); + } else { + button1.Bounds = AABB2(0.f, 0.f, size.x, ButtonSize); + button2.Bounds = AABB2(0.f, size.y - ButtonSize, size.x, ButtonSize); + fill1.Bounds = AABB2(0.f, ButtonSize, size.x, tPos - ButtonSize); + fill2.Bounds = AABB2(0.f, tPos + tLen, size.x, size.y - ButtonSize - tPos - tLen); + trackBar.Bounds = AABB2(0.f, tPos, size.x, tLen); + } + } + + void OnResized() { + Layout(); + UIElement::OnResized(); + } + + float Length { + get { + if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + return Size.x; + } else { + return Size.y; + } + } + } + + float TrackBarAreaLength { + get { + return Length - ButtonSize - ButtonSize; + } + } + + float TrackBarLength { + get { + return Max(TrackBarAreaLength * + (LargeChange / (MaxValue - MinValue + LargeChange)), 40.f); + } + } + + float TrackBarMovementRange { + get { + return TrackBarAreaLength - TrackBarLength; + } + } + + float TrackBarPosition { + get { + if(MaxValue == MinValue) { + return ButtonSize; + } + return float((Value - MinValue) / (MaxValue - MinValue) * TrackBarMovementRange) + ButtonSize; + } + } + + void Render() { + Layout(); + + ScrollBarBase::Render(); + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/Slider.as b/Resources/Scripts/Gui/UIFramework/Slider.as new file mode 100644 index 00000000..263e09ea --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/Slider.as @@ -0,0 +1,208 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "ScrollBar.as" + +namespace spades { + namespace ui { + + class SliderKnob: UIElement { + private Slider@ slider; + private bool dragging = false; + private double startValue; + private float startCursorPos; + private bool hover = false; + + SliderKnob(Slider@ slider) { + super(slider.Manager); + @this.slider = slider; + IsMouseInteractive = true; + } + + private float GetCursorPos(Vector2 pos) { + return pos.x + Position.x; + } + + void MouseDown(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + dragging = true; + startValue = slider.Value; + startCursorPos = GetCursorPos(clientPosition); + } + void MouseMove(Vector2 clientPosition) { + if(dragging) { + double val = startValue; + float delta = GetCursorPos(clientPosition) - startCursorPos; + val += delta * (slider.MaxValue - slider.MinValue) / + double(slider.TrackBarMovementRange); + slider.ScrollTo(val); + } + } + void MouseUp(MouseButton button, Vector2 clientPosition) { + if(button != spades::ui::MouseButton::LeftMouseButton) { + return; + } + dragging = false; + } + void MouseEnter() { + hover = true; + UIElement::MouseEnter(); + } + void MouseLeave() { + hover = false; + UIElement::MouseLeave(); + } + + void Render() { + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + + if (hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.5f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); + } + renderer.DrawImage(img, + AABB2(pos.x + size.x * 0.5f - 3.f, pos.y, + 6.f, size.y)); + + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.6f); + renderer.DrawImage(img, + AABB2(pos.x + size.x * 0.5f - 2.f, pos.y + 1.f, + 4.f, size.y - 2.f)); + } + } + + class Slider: ScrollBarBase { + + private SliderKnob@ knob; + private ScrollBarFill@ fill1; + private ScrollBarFill@ fill2; + + Slider(UIManager@ manager) { + super(manager); + + @knob = SliderKnob(this); + AddChild(knob); + + @fill1 = ScrollBarFill(this, false); + @fill1.Activated = EventHandler(this.LargeDown); + AddChild(fill1); + @fill2 = ScrollBarFill(this, true); + @fill2.Activated = EventHandler(this.LargeUp); + AddChild(fill2); + + } + + private void LargeDown(UIElement@ e) { + ScrollBy(-LargeChange); + } + private void LargeUp(UIElement@ e) { + ScrollBy(LargeChange); + }/* + private void SmallDown(UIElement@ e) { + ScrollBy(-SmallChange); + } + private void SmallUp(UIElement@ e) { + ScrollBy(SmallChange); + }*/ + + void OnChanged() { + Layout(); + ScrollBarBase::OnChanged(); + Layout(); + } + + void Layout() { + Vector2 size = Size; + float tPos = TrackBarPosition; + float tLen = TrackBarLength; + fill1.Bounds = AABB2(0.f, 0.f, tPos, size.y); + fill2.Bounds = AABB2(tPos + tLen, 0.f, size.x - tPos - tLen, size.y); + knob.Bounds = AABB2(tPos, 0.f, tLen, size.y); + } + + void OnResized() { + Layout(); + UIElement::OnResized(); + } + + float Length { + get { + if(Orientation == spades::ui::ScrollBarOrientation::Horizontal) { + return Size.x; + } else { + return Size.y; + } + } + } + + float TrackBarAreaLength { + get { + return Length; + } + } + + float TrackBarLength { + get { + return 16.f; + } + } + + float TrackBarMovementRange { + get { + return TrackBarAreaLength - TrackBarLength; + } + } + + float TrackBarPosition { + get { + return float((Value - MinValue) / (MaxValue - MinValue) * TrackBarMovementRange); + } + } + + void Render() { + Layout(); + + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.1f); + renderer.DrawImage(img, + AABB2(pos.x, pos.y + size.y * 0.5f - 3.f, + size.x, 6.f)); + + renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 0.2f); + renderer.DrawImage(img, + AABB2(pos.x + 1.f, pos.y + size.y * 0.5f - 2.f, + size.x - 2.f, 4.f)); + + ScrollBarBase::Render(); + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/TabStrip.as b/Resources/Scripts/Gui/UIFramework/TabStrip.as new file mode 100644 index 00000000..e7f9698b --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/TabStrip.as @@ -0,0 +1,115 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "Button.as" + +namespace spades { + namespace ui { + + class SimpleTabStripItem: ButtonBase { + UIElement@ linkedElement; + + SimpleTabStripItem(UIManager@ manager, UIElement@ linkedElement) { + super(manager); + @this.linkedElement = linkedElement; + } + + void MouseDown(MouseButton button, Vector2 clientPosition) { + PlayActivateSound(); + OnActivated(); + } + + void Render() { + this.Toggled = linkedElement.Visible; + + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Vector4 textColor(0.9f, 0.9f, 0.9f, 1.0f); + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + if(Toggled) { + renderer.ColorNP = Vector4(0.9f, 0.9f, 0.9f, 1.0f); + textColor = Vector4(0.0f, 0.0f, 0.0f, 1.0f); + } else if(Hover) { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f); + } else { + renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.0f); + } + renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y)); + + Vector2 txtSize = Font.Measure(Caption); + Font.Draw(Caption, pos + (size - txtSize) * 0.5f, 1.f, textColor); + } + } + + class SimpleTabStrip: UIElement { + private float nextX = 0.f; + + EventHandler@ Changed; + + SimpleTabStrip(UIManager@ manager) { + super(manager); + } + + private void OnChanged() { + if(Changed !is null) { + Changed(this); + } + } + + private void OnItemActivated(UIElement@ sender) { + SimpleTabStripItem@ item = cast(sender); + UIElement@ linked = item.linkedElement; + UIElement@[]@ children = this.GetChildren(); + for(uint i = 0, count = children.length; i < count; i++) { + SimpleTabStripItem@ otherItem = cast(children[i]); + otherItem.linkedElement.Visible = (otherItem.linkedElement is linked); + } + OnChanged(); + } + + void AddItem(string title, UIElement@ linkedElement) { + SimpleTabStripItem item(this.Manager, linkedElement); + item.Caption = title; + float w = this.Font.Measure(title).x + 18.f; + item.Bounds = AABB2(nextX, 0.f, w, 24.f); + nextX += w + 4.f; + + @item.Activated = EventHandler(this.OnItemActivated); + + this.AddChild(item); + + } + + void Render() { + UIElement::Render(); + + Renderer@ renderer = Manager.Renderer; + Vector2 pos = ScreenPosition; + Vector2 size = Size; + Image@ img = renderer.RegisterImage("Gfx/White.tga"); + renderer.ColorNP = Vector4(0.9f, 0.9f, 0.9f, 1.0f); + renderer.DrawImage(img, AABB2(pos.x, pos.y + 24.f, size.x, 1.f)); + } + + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/TextViewer.as b/Resources/Scripts/Gui/UIFramework/TextViewer.as new file mode 100644 index 00000000..bbbba8c0 --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/TextViewer.as @@ -0,0 +1,138 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "Label.as" + +namespace spades { + namespace ui { + + class TextViewerModel: ListViewModel { + UIManager@ manager; + string[]@ lines = array(); + Vector4[]@ colors = array(); + Font@ font; + float width; + void AddLine(string text, Vector4 color) { + int startPos = 0; + if(font.Measure(text).x <= width) { + lines.insertLast(text); + colors.insertLast(color); + return; + } + + int pos = 0; + int len = int(text.length); + bool charMode = false; + while(startPos < len) { + int nextPos = pos + 1; + if(charMode) { + // skip to the next UTF-8 character boundary + while(nextPos < len && ((text[nextPos] & 0x80) != 0) && + ((text[nextPos] & 0xc0) != 0xc0)) + nextPos++; + } else { + while(nextPos < len && text[nextPos] != 0x20) + nextPos++; + } + if(font.Measure(text.substr(startPos, nextPos - startPos)).x > width) { + if(pos == startPos) { + if(charMode) { + pos = nextPos; + }else{ + charMode = true; + } + continue; + }else{ + lines.insertLast(text.substr(startPos, pos - startPos)); + colors.insertLast(color); + startPos = pos; + while(startPos < len && text[startPos] == 0x20) + startPos++; + pos = startPos; + charMode = false; + continue; + } + }else{ + pos = nextPos; + if(nextPos >= len) { + lines.insertLast(text.substr(startPos, nextPos - startPos)); + colors.insertLast(color); + break; + } + } + } + + } + TextViewerModel(UIManager@ manager, string text, Font@ font, float width) { + @this.manager = manager; + @this.font = font; + this.width = width; + string[]@ lines = text.split("\n"); + for(uint i = 0; i < lines.length; i++) + AddLine(lines[i], Vector4(1.f, 1.f, 1.f, 1.f)); + } + int NumRows { get { return int(lines.length); } } + UIElement@ CreateElement(int row) { + Label i(manager); + i.Text = lines[row]; + i.TextColor = colors[row]; + return i; + } + void RecycleElement(UIElement@ elem) {} + } + + class TextViewer: ListViewBase { + private string text; + private TextViewerModel@ textmodel; + + TextViewer(UIManager@ manager) { + super(manager); + } + + /** Sets the displayed text. Ensure TextViewer.Font is not null before setting this proeprty. */ + string Text { + get final { return text; } + set { + text = value; + @textmodel = TextViewerModel(Manager, text, Font, ItemWidth); + @Model = textmodel; + } + } + + void AddLine(string line, bool autoscroll = false, Vector4 color = Vector4(1.f, 1.f, 1.f, 1.f)) { + if(textmodel is null) { + this.Text = ""; + } + if(autoscroll){ + this.Layout(); + if(this.scrollBar.Value < this.scrollBar.MaxValue) { + autoscroll = false; + } + } + textmodel.AddLine(line, color); + if(autoscroll) { + this.Layout(); + this.ScrollToEnd(); + } + } + } + + } +} diff --git a/Resources/Scripts/Gui/UIFramework/UIControls.as b/Resources/Scripts/Gui/UIFramework/UIControls.as new file mode 100644 index 00000000..00221720 --- /dev/null +++ b/Resources/Scripts/Gui/UIFramework/UIControls.as @@ -0,0 +1,28 @@ +/* + Copyright (c) 2013 yvt + + This file is part of OpenSpades. + + OpenSpades is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenSpades is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenSpades. If not, see . + + */ + +#include "Button.as" +#include "Field.as" +#include "Label.as" +#include "ListView.as" +#include "ScrollBar.as" +#include "Slider.as" +#include "TabStrip.as" +#include "TextViewer.as" diff --git a/Resources/Scripts/Gui/UIFramework.as b/Resources/Scripts/Gui/UIFramework/UIFramework.as similarity index 100% rename from Resources/Scripts/Gui/UIFramework.as rename to Resources/Scripts/Gui/UIFramework/UIFramework.as