Skip to content

Commit

Permalink
Big update
Browse files Browse the repository at this point in the history
* Change design to use box characters
* Improved feature API
* Private messaging revamped
* Full config editor
* New tab screen has buttons
* misc changes
  • Loading branch information
simon816 committed Jul 15, 2016
1 parent 5b35c6f commit 5162c11
Show file tree
Hide file tree
Showing 20 changed files with 1,151 additions and 193 deletions.
14 changes: 14 additions & 0 deletions src/main/java/com/simon816/minecraft/tabchat/AbstractFeature.java
@@ -0,0 +1,14 @@
package com.simon816.minecraft.tabchat;

public abstract class AbstractFeature {

protected void onInit() {
}

protected void onNewPlayerView(PlayerChatView view) {
}

protected void onViewClose(PlayerChatView view) {
}

}

This file was deleted.

Expand Up @@ -6,7 +6,7 @@

import java.util.List;

public class MessagePipeline implements MessageHandler /*Implements because why not */ {
public class MessagePipeline implements MessageHandler {

private final List<MessageHandler> handlers = Lists.newArrayList();

Expand Down
23 changes: 18 additions & 5 deletions src/main/java/com/simon816/minecraft/tabchat/PlayerChatView.java
@@ -1,5 +1,6 @@
package com.simon816.minecraft.tabchat;

import com.google.common.base.Objects;
import com.simon816.minecraft.tabchat.tabs.GlobalTab;
import com.simon816.minecraft.tabchat.tabs.NewTab;
import com.simon816.minecraft.tabchat.tabs.TextBufferTab;
Expand Down Expand Up @@ -33,6 +34,7 @@ public PlayerChatView(Player player) {
return true;
});
this.incomingPipeline.addHandler((message, sender) -> {
this.window.getActiveTab().onTextEntered(this, message);
// Only allow normal chat to get processed if on the global tab
return this.window.getActiveTab() != this.globalTab;
});
Expand Down Expand Up @@ -62,16 +64,24 @@ public MessagePipeline getOutgoingPipeline() {
}

public boolean handleIncoming(Text message) {
return this.incomingPipeline.process(message, this.playerContext.player);
try {
return this.incomingPipeline.process(message, this.playerContext.player);
} catch (Exception e) {
e.printStackTrace();
return true; // Just ignore the input
}
}

public Optional<Text> transformOutgoing(CommandSource sender, Text originalOutgoing, ChatType type) {
if (this.isUpdating) {
// Send it straight out
return Optional.of(originalOutgoing);
}
// pump the text through the outgoing pipeline and redraw
if (this.outgoingPipeline.process(originalOutgoing, sender)) {
return Optional.of(this.window.draw(this.playerContext));
}
// Text ignored
return Optional.empty();
}

Expand All @@ -83,15 +93,18 @@ boolean handleCommand(String[] args) {
this.window.removeTab(Integer.parseInt(args[1]));
} else if (cmd.equals("newtab")) {
this.window.addTab(new NewTab(), true);
} else if (cmd.equals("tableft")) {
this.window.shiftLeft();
} else if (cmd.equals("tabright")) {
this.window.shiftRight();
} else {
return false;
}
update();
return true;
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("player", this.playerContext)
.add("window", this.window)
.toString();
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/simon816/minecraft/tabchat/PlayerContext.java
@@ -1,5 +1,8 @@
package com.simon816.minecraft.tabchat;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Objects;
import org.spongepowered.api.entity.living.player.Player;

public class PlayerContext {
Expand All @@ -15,7 +18,16 @@ public PlayerContext(Player player, int width, int height) {
}

public PlayerContext withHeight(int height) {
checkArgument(height >= 1, "Height must be at least one");
return new PlayerContext(this.player, this.width, height);
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("player", this.player)
.add("width", this.width)
.add("height", this.height)
.toString();
}
}
25 changes: 19 additions & 6 deletions src/main/java/com/simon816/minecraft/tabchat/TabbedChat.java
@@ -1,5 +1,7 @@
package com.simon816.minecraft.tabchat;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.simon816.minecraft.tabchat.channel.WrapOutputChannel;
Expand Down Expand Up @@ -34,14 +36,19 @@
public class TabbedChat {

private final Map<UUID, PlayerChatView> playerViewMap = Maps.newHashMap();
private final List<FeatureLoader> features = Lists.newArrayList();
private final List<AbstractFeature> features = Lists.newArrayList();

private static TabbedChat instance;

public static TabbedChat instance() {
return instance;
}

public static PlayerChatView getView(CommandSource source) {
checkArgument(source instanceof Player);
return getView((Player) source);
}

public static PlayerChatView getView(Player player) {
return getView(player.getUniqueId());
}
Expand All @@ -62,11 +69,13 @@ public void onPreInit(GamePreInitializationEvent event) {
@Listener
public void onInit(GameInitializationEvent event) {
Sponge.getGame().getCommandManager().register(this, new TabbedChatCommand(), "tabchat");
this.addFeature(new PrivateMessageFeature.Loader());

this.addFeature(new PrivateMessageFeature());
}

public void addFeature(FeatureLoader loader) {
this.features.add(loader);
public void addFeature(AbstractFeature feature) {
this.features.add(feature);
feature.onInit();
}

@Listener(order = Order.POST)
Expand All @@ -88,7 +97,11 @@ public void onPlayerJoin(ClientConnectionEvent.Join event) {

@Listener
public void onPlayerQuit(ClientConnectionEvent.Disconnect event) {
this.playerViewMap.remove(event.getTargetEntity().getUniqueId());
PlayerChatView view = this.playerViewMap.remove(event.getTargetEntity().getUniqueId());
for (AbstractFeature feature : this.features) {
feature.onViewClose(view);
}
view.getWindow().closeAll();
// TODO Offline message buffering?
}

Expand Down Expand Up @@ -119,7 +132,7 @@ public void onOutgoingMessage(MessageChannelEvent event) {
}

void loadFeatures(PlayerChatView view) {
for (FeatureLoader feature : this.features) {
for (AbstractFeature feature : this.features) {
feature.onNewPlayerView(view);
}
}
Expand Down
Expand Up @@ -21,7 +21,7 @@ public CommandResult process(CommandSource source, String arguments) throws Comm
if (!(source instanceof Player)) {
throw new CommandException(Text.of("Source must be player"));
}
if (TabbedChat.getView((Player) source).handleCommand(arguments.split("\\s+"))) {
if (TabbedChat.getView(source).handleCommand(arguments.split("\\s+"))) {
return CommandResult.success();
}
throw new CommandNotFoundException(arguments);
Expand Down
154 changes: 78 additions & 76 deletions src/main/java/com/simon816/minecraft/tabchat/Window.java
@@ -1,5 +1,6 @@
package com.simon816.minecraft.tabchat;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.simon816.minecraft.tabchat.tabs.Tab;
import com.simon816.minecraft.tabchat.util.TextBuffer;
Expand Down Expand Up @@ -52,111 +53,112 @@ public void setTab(int tabIndex) {
this.tabs.get(this.activeIndex).onFocus();
}

public void setTab(Tab tab) {
setTab(this.tabs.indexOf(tab));
}

public Tab getActiveTab() {
return this.tabs.get(this.activeIndex);
}

public int getActiveIndex() {
return this.activeIndex;
}

@Override
public Text draw(PlayerContext ctx) {
if (ctx.height < 5) {
throw new IllegalArgumentException("Height too small");
}
StringBuilder borderBuilder = new StringBuilder();
double borderWidth = 0;
while (borderWidth <= ctx.width) {
borderWidth += TextUtils.getWidth('=', false);
if (borderWidth <= ctx.width) {
borderBuilder.append('=');
}
}
String borderLine = borderBuilder.toString();
Builder builder = Text.builder();
builder.append(getTabListText(ctx.width));
builder.append(Text.of(borderLine));
builder.append(Text.NEW_LINE);
builder.append(getActiveTab().draw(ctx.withHeight(ctx.height - 4)));
builder.append(Text.of(borderLine + "\n"));
builder.append(getStatusBarText());
builder.append(getActiveTab().draw(ctx.withHeight(ctx.height - this.tabListLines - 1)));
builder.append(getStatusBarText(ctx.width));
return builder.build();
}

private Text getStatusBarText() {
return Text.of("Status: ", TextColors.RED, "0", TextColors.RESET, " unread PMs");
private Text getStatusBarText(int width) {
Text line = Text.of("╚Status: ", TextColors.RED, "0", TextColors.RESET, " unread PMs");
return Text.builder().append(line, TextUtils.repeatAndTerminate('═', '╝', width - TextUtils.getWidth(line))).build();
}

private static final LiteralText leftArrow = Text.builder("< ").onClick(TabbedChat.command("tableft")).build();
private static final LiteralText rightArrow = Text.builder(" >").onClick(TabbedChat.command("tabright")).build();
private static final LiteralText newTab = Text.builder(" [+]").color(TextColors.GREEN).onClick(TabbedChat.command("newtab"))
.onHover(TextActions.showText(Text.of("New Tab"))).build();
private int displayTabIndex;

public void shiftLeft() {
if (--this.displayTabIndex < 0) {
this.displayTabIndex = 0;
}
}
private static final LiteralText newTab = Text.builder("[+]")
.color(TextColors.GREEN)
.onClick(TabbedChat.command("newtab"))
.onHover(TextActions.showText(Text.of("New Tab")))
.build();

public void shiftRight() {
if (++this.displayTabIndex >= this.tabs.size()) {
this.displayTabIndex--;
}
}
private int tabListLines;

private Text getTabListText(int maxWidth) {
Text.Builder builder = Text.builder();
int width = TextUtils.getWidth(leftArrow) + TextUtils.getWidth(rightArrow);
int currentLineWidth = 0;
TextBuffer buffer = new TextBuffer();
int count = 0;
for (int i = this.displayTabIndex; i < this.tabs.size(); i++) {
buffer.append(TextUtils.charCache('╔'));
this.tabListLines = 1;
for (int i = 0; i < this.tabs.size(); i++) {
Tab tab = this.tabs.get(i);

buffer.append(Text.of("| "));

Text.Builder button = tab.getTitle().toBuilder();
button.color(TextColors.GREEN);
if (i == this.activeIndex) {
button.style(TextStyles.BOLD);
} else {
button.onClick(TabbedChat.command("settab " + i));
}

buffer.append(button.build());

buffer.append(createTabButton(i));
if (tab.hasCloseButton()) {
Text closeButton = Text.builder("[x]").color(TextColors.RED)
.onClick(TabbedChat.command("closetab " + i))
.onHover(TextActions.showText(Text.of("Close")))
.build();
buffer.append(closeButton);
}
buffer.append(Text.of(" |"));

width += buffer.getWidth();
if (width <= maxWidth) {
builder.append(buffer.getContents());
count++;
buffer.clear();
} else {
buffer.clear();
break;
buffer.append(createCloseButton(i));
}
buffer.append(TextUtils.charCache('═'));
currentLineWidth = addTabElement(buffer, builder, currentLineWidth, maxWidth);
buffer.clear();
}
width += TextUtils.getWidth(newTab);
if (this.displayTabIndex == 0 && width <= maxWidth) {
builder.append(newTab);
buffer.append(newTab);
currentLineWidth = addTabElement(buffer, builder, currentLineWidth, maxWidth);
builder.append(TextUtils.repeatAndTerminate('═', this.tabListLines == 1 ? '╗' : '╣', maxWidth - currentLineWidth));
return builder.build();
}

private int addTabElement(TextBuffer buffer, Text.Builder builder, int currentLineWidth, int maxWidth) {
if (currentLineWidth + buffer.getWidth() > maxWidth) {
// Overspilled - finish this line and move to another one
builder.append(TextUtils.repeatAndTerminate('═', this.tabListLines == 1 ? '╗' : '╣', maxWidth - currentLineWidth));
Text newLineStart = Text.of("\n╠");
currentLineWidth = TextUtils.getWidth(newLineStart);
builder.append(newLineStart);
this.tabListLines++;
}
currentLineWidth += buffer.getWidth();
builder.append(buffer.getContents());
return currentLineWidth;
}

private Text createTabButton(int tabIndex) {
Text.Builder button = this.tabs.get(tabIndex).getTitle().toBuilder();
button.color(TextColors.GREEN);
if (tabIndex == this.activeIndex) {
button.style(TextStyles.BOLD);
} else {
if (this.displayTabIndex > 0) {
builder.insert(0, leftArrow);
}
if (this.displayTabIndex + count < this.tabs.size() || width > maxWidth) {
builder.append(rightArrow);
}
if (width <= maxWidth) {
builder.append(newTab);
}
button.onClick(TabbedChat.command("settab " + tabIndex));
}
return button.build();
}

return builder.build();
private Text createCloseButton(int tabIndex) {
return Text.builder("[x]").color(TextColors.RED)
.onClick(TabbedChat.command("closetab " + tabIndex))
.onHover(TextActions.showText(Text.of("Close")))
.build();
}

void closeAll() {
for (Tab tab : this.tabs) {
tab.onClose();
}
this.tabs.clear();
this.activeIndex = -1;
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("active", getActiveTab())
.add("tabs", this.tabs)
.toString();
}

}

0 comments on commit 5162c11

Please sign in to comment.