Chat log viewer

This commit is contained in:
yvt 2013-12-31 16:29:10 +09:00
parent 2fdf763481
commit 3fd9bbeae7
5 changed files with 350 additions and 39 deletions

View File

@ -30,6 +30,8 @@ namespace spades {
spades::ui::UIManager@ manager;
spades::ui::UIElement@ activeUI;
ChatLogWindow@ chatLogWindow;
ClientMenu@ clientMenu;
bool shouldExit = false;
@ -48,6 +50,7 @@ namespace spades {
@clientMenu = ClientMenu(this);
clientMenu.Bounds = manager.RootElement.Bounds;
@chatLogWindow = ChatLogWindow(this);
}
void MouseEvent(float x, float y) {
@ -143,6 +146,10 @@ namespace spades {
void CloseUI() {
@ActiveUI = null;
}
void RecordChatLog(string text, Vector4 color) {
chatLogWindow.Record(text, color);
}
}
class ClientMenu: spades::ui::UIElement {
@ -154,7 +161,7 @@ namespace spades {
@this.ui = ui;
@this.helper = ui.helper;
float winW = 180.f, winH = 94.f;
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;
@ -181,15 +188,22 @@ namespace spades {
}
{
spades::ui::Button button(Manager);
button.Caption = "Setup";
button.Caption = "Chat Log";
button.Bounds = AABB2(winX, winY + 32.f, winW, 30.f);
button.Activated = EventHandler(this.OnChatLog);
AddChild(button);
}
{
spades::ui::Button button(Manager);
button.Caption = "Setup";
button.Bounds = AABB2(winX, winY + 64.f, winW, 30.f);
button.Activated = EventHandler(this.OnSetup);
AddChild(button);
}
{
spades::ui::Button button(Manager);
button.Caption = "Disconnect";
button.Bounds = AABB2(winX, winY + 64.f, winW, 30.f);
button.Bounds = AABB2(winX, winY + 96.f, winW, 30.f);
button.Activated = EventHandler(this.OnDisconnect);
AddChild(button);
}
@ -205,6 +219,10 @@ namespace spades {
PreferenceView al(this, opt);
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;
}
@ -298,32 +316,41 @@ namespace spades {
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) {
teamButton.Toggled = false;
globalButton.Toggled = true;
isTeamChat = false;
UpdateState();
IsTeamChat = false;
}
private void OnSetTeam(spades::ui::UIElement@ sender) {
teamButton.Toggled = true;
globalButton.Toggled = false;
isTeamChat = true;
UpdateState();
IsTeamChat = true;
}
private void OnFieldChanged(spades::ui::UIElement@ sender) {
UpdateState();
}
private void OnCancel(spades::ui::UIElement@ sender) {
private void Close() {
@ui.ActiveUI = null;
}
private void OnCancel(spades::ui::UIElement@ sender) {
Close();
}
private void OnSay(spades::ui::UIElement@ sender) {
if(isTeamChat)
ui.helper.SayTeam(field.Text);
else
ui.helper.SayGlobal(field.Text);
@ui.ActiveUI = null;
Close();
}
void HotKey(string key) {
@ -341,6 +368,186 @@ 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 = "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 = "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 = "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,
Font@ font, ClientUIHelper@ helper) {
return ClientUI(renderer, audioDevice, font, helper);

View File

@ -227,6 +227,9 @@ namespace spades {
}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);
@ -239,8 +242,13 @@ namespace spades {
size -= Vector2(16.f, 16.f);
txtPos = pos + (size - txtSize) * Alignment;
font.DrawShadow(text, txtPos, 1.f,
Vector4(1.f, 1.f, 1.f, 1.f), Vector4(0.f, 0.f, 0.f, 0.4f));
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));
}
}
}
@ -1367,24 +1375,31 @@ namespace spades {
void ScrollToTop() {
scrollBar.ScrollTo(0.0);
}
void ScrollToEnd() {
scrollBar.ScrollTo(scrollBar.MaxValue);
}
}
class TextViewerModel: ListViewModel {
UIManager@ manager;
string[]@ lines = array<string>();
Vector4[]@ colors = array<spades::Vector4>();
Font@ font;
float width;
private void AddLine(string text) {
void AddLine(string text, Vector4 color) {
int startPos = 0;
int minEnd = 1, maxEnd = text.length;
if(font.Measure(text).x <= width) {
lines.insertLast(text);
colors.insertLast(color);
return;
}
// find line-break point by binary search (O(n log n))
while(startPos < int(text.length)) {
if(minEnd >= maxEnd) {
lines.insertLast(text.substr(startPos, maxEnd - startPos));
colors.insertLast(color);
startPos = maxEnd;
minEnd = startPos + 1;
maxEnd = text.length;
@ -1404,12 +1419,13 @@ namespace spades {
this.width = width;
string[]@ lines = text.split("\n");
for(uint i = 0; i < lines.length; i++)
AddLine(lines[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) {}
@ -1440,6 +1456,7 @@ namespace spades {
class TextViewer: ListViewBase {
private string text;
private TextViewerModel@ textmodel;
TextViewer(UIManager@ manager) {
super(manager);
@ -1450,7 +1467,25 @@ namespace spades {
get final { return text; }
set {
text = value;
@Model = TextViewerModel(Manager, text, Font, ItemWidth);
@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

@ -2691,13 +2691,32 @@ namespace spades {
void Client::PlayerSentChatMessage(spades::client::Player *p,
bool global,
const std::string &msg){
std::string s;
if(global)
s = "[Global] ";
s += ChatWindow::TeamColorMessage(p->GetName(), p->GetTeamId());
s += ": ";
s += msg;
chatWindow->AddMessage(s);
{
std::string s;
if(global)
s = "[Global] ";
s += ChatWindow::TeamColorMessage(p->GetName(), p->GetTeamId());
s += ": ";
s += msg;
chatWindow->AddMessage(s);
}
{
std::string s;
if(global)
s = "[Global] ";
s += p->GetName();
s += ": ";
s += msg;
auto col = p->GetTeamId() < 2 ?
world->GetTeam(p->GetTeamId()).color :
IntVector3::Make(255, 255, 255);
scriptedUI->RecordChatLog(s,
MakeVector4(col.x / 255.f, col.y / 255.f,
col.z / 255.f, 0.8f));
}
if(global)
NetLog("[Global] %s (%s): %s",
p->GetName().c_str(),
@ -2709,6 +2728,7 @@ namespace spades {
world->GetTeam(p->GetTeamId()).name.c_str(),
msg.c_str());
if((!IsMuted()) && (int)cg_chatBeep) {
Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Chat.wav");
audioDevice->PlayLocal(chunk, AudioParam());
@ -2718,9 +2738,7 @@ namespace spades {
void Client::ServerSentMessage(const std::string &msg) {
chatWindow->AddMessage(msg);
NetLog("%s", msg.c_str());
if(msg.find(playerName) != std::string::npos){
printf("Mention: %s\n", msg.c_str());
}
scriptedUI->RecordChatLog(msg, Vector4::Make(1.f, 1.f, 1.f, 0.8f));
}
#pragma mark - Follow / Spectate
@ -3359,20 +3377,52 @@ namespace spades {
}
void Client::PlayerLeaving(spades::client::Player *p) {
std::string msg;
msg = "Player " + chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
msg += " has left";
chatWindow->AddMessage(msg);
{
std::string msg;
msg = "Player " + chatWindow->TeamColorMessage(p->GetName(), p->GetTeamId());
msg += " has left";
chatWindow->AddMessage(msg);
}
{
std::string msg;
msg = "Player " + p->GetName();
msg += " has left";
auto col = p->GetTeamId() < 2 ?
world->GetTeam(p->GetTeamId()).color :
IntVector3::Make(255, 255, 255);
scriptedUI->RecordChatLog(msg,
MakeVector4(col.x / 255.f, col.y / 255.f,
col.z / 255.f, 0.8f));
}
}
void Client::PlayerJoinedTeam(spades::client::Player *p) {
std::string msg;
msg = p->GetName();
msg += " joined ";
msg += chatWindow->TeamColorMessage(world->GetTeam(p->GetTeamId()).name,
p->GetTeamId());
msg += " team";
chatWindow->AddMessage(msg);
{
std::string msg;
msg = p->GetName();
msg += " joined ";
msg += chatWindow->TeamColorMessage(world->GetTeam(p->GetTeamId()).name,
p->GetTeamId());
msg += " team";
chatWindow->AddMessage(msg);
}
{
std::string msg;
msg = p->GetName();
msg += " joined ";
msg += world->GetTeam(p->GetTeamId()).name;
msg += " team";
auto col = p->GetTeamId() < 2 ?
world->GetTeam(p->GetTeamId()).color :
IntVector3::Make(255, 255, 255);
scriptedUI->RecordChatLog(msg,
MakeVector4(col.x / 255.f, col.y / 255.f,
col.z / 255.f, 0.8f));
}
}
void Client::GrenadeDestroyedBlock(spades::IntVector3 blk){
@ -3405,6 +3455,8 @@ namespace spades {
NetLog("%s", msg.c_str());
centerMessageView->AddMessage(msg);
scriptedUI->RecordChatLog(msg, MakeVector4(1.f, 1.f, 1.f, 0.8f));
if(world->GetLocalPlayer()){
if(teamId == world->GetLocalPlayer()->GetTeamId()){
Handle<IAudioChunk> chunk = audioDevice->RegisterSound("Sounds/Feedback/Win.wav");

View File

@ -212,6 +212,21 @@ namespace spades {
c.ExecuteChecked();
}
void ClientUI::RecordChatLog(const std::string &msg,
Vector4 color) {
SPADES_MARK_FUNCTION();
if(!ui){
return;
}
static ScriptFunction func("ClientUI", "void RecordChatLog(string, Vector4)");
ScriptContextHandle c = func.Prepare();
std::string k = msg;
c->SetObject(&*ui);
c->SetArgObject(0, reinterpret_cast<void*>(&k));
c->SetArgObject(1, reinterpret_cast<void*>(&color));
c.ExecuteChecked();
}
void ClientUI::EnterClientMenu() {
SPADES_MARK_FUNCTION();
if(!ui){

View File

@ -73,6 +73,8 @@ namespace spades {
bool NeedsInput();
void RecordChatLog(const std::string&, Vector4 color);
void EnterClientMenu();
void EnterGlobalChatWindow();
void EnterTeamChatWindow();