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