Refactor AngelScript code

Split huge source files
This commit is contained in:
yvt 2017-12-30 22:06:38 +09:00
parent 604d82eee4
commit 8e7cf01492
31 changed files with 5774 additions and 5331 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<spades::ui::CommandHistoryItem@>@ 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);
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<spades::ui::CommandHistoryItem@> 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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<spades::ui::CommandHistoryItem@>@ cmdhistory;
CommandHistoryItem@ temporalLastHistory;
uint currentHistoryIndex;
FieldWithHistory(spades::ui::UIManager@ manager, array<spades::ui::CommandHistoryItem@>@ 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();
}
};
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<spades::ui::CommandHistoryItem@> 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<spades::ui::CommandHistoryItem@>@ cmdhistory;
CommandHistoryItem@ temporalLastHistory;
uint currentHistoryIndex;
FieldWithHistory(spades::ui::UIManager@ manager, array<spades::ui::CommandHistoryItem@>@ 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<spades::ui::CommandHistoryItem@>@ 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);
}
}

View File

@ -1,27 +1,26 @@
/* /*
Copyright (c) 2013 yvt Copyright (c) 2013 yvt
This file is part of OpenSpades. This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
OpenSpades is distributed in the hope that it will be useful, OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>. along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "UIFramework.as" #include "UIFramework/UIFramework.as"
#include "UIControls.as"
#include "MessageBox.as" #include "MessageBox.as"
#include "MainScreen.as" #include "MainScreen/MainScreenUI.as"
#include "StartupScreen.as" #include "StartupScreen/StartupScreenUI.as"
#include "Preferences.as" #include "Preferences.as"
#include "ClientUI.as" #include "Client/ClientUI.as"

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<ServerListItem>(sender);
if(ItemActivated !is null) {
ItemActivated(this, item.item);
}
}
private void OnItemDoubleClicked(spades::ui::UIElement@ sender){
ServerListItem@ item = cast<ServerListItem>(sender);
if(ItemDoubleClicked !is null) {
ItemDoubleClicked(this, item.item);
}
}
private void OnItemRightClicked(spades::ui::UIElement@ sender){
ServerListItem@ item = cast<ServerListItem>(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<spades::MainScreenServerItem@>();
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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<spades::MainScreenServerItem@>();
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();
}
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<ServerListItem>(sender);
if(ItemActivated !is null) {
ItemActivated(this, item.item);
}
}
private void OnItemDoubleClicked(spades::ui::UIElement@ sender){
ServerListItem@ item = cast<ServerListItem>(sender);
if(ItemDoubleClicked !is null) {
ItemDoubleClicked(this, item.item);
}
}
private void OnItemRightClicked(spades::ui::UIElement@ sender){
ServerListItem@ item = cast<ServerListItem>(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));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<StartupScreenConfigItemEditor>(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<StartupScreenComplexConfig>(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<StartupScreenConfigItemEditor>(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<StartupScreenConfigItemEditor>(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<spades::ui::UIElement>(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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<ConfirmScreen>(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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}

View File

@ -18,7 +18,7 @@
*/ */
#include "UIControls.as" #include "../UIFramework/UIControls.as"
namespace spades { namespace spades {
class UpdateCheckView: spades::ui::UIElement { class UpdateCheckView: spades::ui::UIElement {
@ -150,4 +150,4 @@ namespace spades {
OpenUpdateInfoURL(this); OpenUpdateInfoURL(this);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<RadioButton>(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));
}
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<SimpleTabStripItem>(sender);
UIElement@ linked = item.linkedElement;
UIElement@[]@ children = this.GetChildren();
for(uint i = 0, count = children.length; i < count; i++) {
SimpleTabStripItem@ otherItem = cast<SimpleTabStripItem>(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));
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "Label.as"
namespace spades {
namespace ui {
class TextViewerModel: ListViewModel {
UIManager@ manager;
string[]@ lines = array<string>();
Vector4[]@ colors = array<spades::Vector4>();
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();
}
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "Button.as"
#include "Field.as"
#include "Label.as"
#include "ListView.as"
#include "ScrollBar.as"
#include "Slider.as"
#include "TabStrip.as"
#include "TextViewer.as"