Merge pull request #462 from EngineHub/feature/expanded-styleset

feat(text): added modern text formatting to worldedit, and switch a few things over to it
This commit is contained in:
Matthew Miller 2019-04-25 19:39:26 +10:00 committed by GitHub
commit db4871f022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 466 additions and 1156 deletions

View File

@ -82,6 +82,14 @@ artifactory {
artifactoryPublish.skip = true artifactoryPublish.skip = true
subprojects { subprojects {
repositories {
mavenCentral()
maven { url "http://maven.sk89q.com/repo/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
}
configure(['core', 'bukkit', 'forge', 'sponge'].collect { project(":worldedit-$it") }) {
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven' apply plugin: 'maven'
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
@ -97,12 +105,6 @@ subprojects {
checkstyle.configFile = new File(rootProject.projectDir, "config/checkstyle/checkstyle.xml") checkstyle.configFile = new File(rootProject.projectDir, "config/checkstyle/checkstyle.xml")
checkstyle.toolVersion = '7.6.1' checkstyle.toolVersion = '7.6.1'
repositories {
mavenCentral()
maven { url "http://maven.sk89q.com/repo/" }
maven { url "http://repo.maven.apache.org/maven2" }
}
if (JavaVersion.current().isJava8Compatible()) { if (JavaVersion.current().isJava8Compatible()) {
// Java 8 turns on doclint which we fail // Java 8 turns on doclint which we fail
tasks.withType(Javadoc) { tasks.withType(Javadoc) {
@ -136,18 +138,6 @@ subprojects {
build.dependsOn(checkstyleTest) build.dependsOn(checkstyleTest)
build.dependsOn(javadocJar) build.dependsOn(javadocJar)
shadowJar {
classifier 'dist'
dependencies {
include(dependency('com.sk89q:jchronic:0.2.4a'))
include(dependency('com.thoughtworks.paranamer:paranamer:2.6'))
include(dependency('com.sk89q.lib:jlibnoise:1.0.0'))
}
exclude 'GradleStart**'
exclude '.cache'
exclude 'LICENSE*'
}
artifactoryPublish { artifactoryPublish {
publishConfigs('archives') publishConfigs('archives')
} }
@ -157,3 +147,17 @@ subprojects {
include '**/*.java' include '**/*.java'
} }
} }
configure(['bukkit', 'forge', 'sponge'].collect { project(":worldedit-$it") }) {
shadowJar {
classifier 'dist'
dependencies {
include(project(":worldedit-libs:core"))
include(project(":worldedit-libs:${project.name.replace("worldedit-", "")}"))
include(project(":worldedit-core"))
}
exclude 'GradleStart**'
exclude '.cache'
exclude 'LICENSE*'
}
}

View File

@ -1,3 +1,8 @@
rootProject.name = 'worldedit' rootProject.name = 'worldedit'
include 'worldedit-core', 'worldedit-bukkit', 'worldedit-forge', 'worldedit-sponge' include 'worldedit-libs'
['bukkit', 'core', 'forge', 'sponge'].forEach {
include "worldedit-libs:$it"
include "worldedit-$it"
}

View File

@ -10,11 +10,12 @@ repositories {
dependencies { dependencies {
compile project(':worldedit-core') compile project(':worldedit-core')
compile project(':worldedit-libs:bukkit')
compile 'com.sk89q:dummypermscompat:1.10' compile 'com.sk89q:dummypermscompat:1.10'
compile 'org.bukkit:bukkit:1.13.2-R0.1-SNAPSHOT' // zzz compile 'org.bukkit:bukkit:1.13.2-R0.1-SNAPSHOT' // zzz
compile 'org.bstats:bstats-bukkit:1.4'
compile "io.papermc:paperlib:1.0.1" compile "io.papermc:paperlib:1.0.1"
compile 'org.slf4j:slf4j-jdk14:1.7.26' compile 'org.slf4j:slf4j-jdk14:1.7.26'
compile 'org.bstats:bstats-bukkit:1.4'
testCompile 'org.mockito:mockito-core:1.9.0-rc1' testCompile 'org.mockito:mockito-core:1.9.0-rc1'
} }

View File

@ -26,6 +26,8 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -91,6 +93,11 @@ public class BukkitCommandSender implements Actor {
} }
} }
@Override
public void print(TextComponent component) {
TextAdapter.sendComponent(sender, component);
}
@Override @Override
public boolean canDestroyBedrock() { public boolean canDestroyBedrock() {
return true; return true;

View File

@ -37,7 +37,8 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.gamemode.GameModes;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -125,6 +126,11 @@ public class BukkitPlayer extends AbstractPlayerActor {
} }
} }
@Override
public void print(TextComponent component) {
TextAdapter.sendComponent(player, component);
}
@Override @Override
public void setPosition(Vector3 pos, float pitch, float yaw) { public void setPosition(Vector3 pos, float pitch, float yaw) {
player.teleport(new Location(player.getWorld(), pos.getX(), pos.getY(), player.teleport(new Location(player.getWorld(), pos.getX(), pos.getY(),

View File

@ -2,15 +2,13 @@ apply plugin: 'eclipse'
apply plugin: 'idea' apply plugin: 'idea'
dependencies { dependencies {
compile project(':worldedit-libs:core')
compile 'de.schlichtherle:truezip:6.8.3' compile 'de.schlichtherle:truezip:6.8.3'
compile 'rhino:js:1.7R2' compile 'rhino:js:1.7R2'
compile 'org.yaml:snakeyaml:1.9' compile 'org.yaml:snakeyaml:1.9'
compile 'com.google.guava:guava:21.0' compile 'com.google.guava:guava:21.0'
compile 'com.sk89q:jchronic:0.2.4a'
compile 'com.google.code.findbugs:jsr305:1.3.9' compile 'com.google.code.findbugs:jsr305:1.3.9'
compile 'com.thoughtworks.paranamer:paranamer:2.6'
compile 'com.google.code.gson:gson:2.8.0' compile 'com.google.code.gson:gson:2.8.0'
compile 'com.sk89q.lib:jlibnoise:1.0.0'
compile 'com.googlecode.json-simple:json-simple:1.1.1' compile 'com.googlecode.json-simple:json-simple:1.1.1'
compile 'org.slf4j:slf4j-api:1.7.26' compile 'org.slf4j:slf4j-api:1.7.26'
//compile 'net.sf.trove4j:trove4j:3.0.3' //compile 'net.sf.trove4j:trove4j:3.0.3'
@ -28,5 +26,3 @@ sourceSets {
} }
} }
} }
build.dependsOn(shadowJar)

View File

@ -55,15 +55,15 @@ import com.sk89q.worldedit.regions.selector.SphereRegionSelector;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
import com.sk89q.worldedit.util.formatting.Style;
import com.sk89q.worldedit.util.formatting.StyledFragment;
import com.sk89q.worldedit.util.formatting.component.CommandListBox; import com.sk89q.worldedit.util.formatting.component.CommandListBox;
import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
import com.sk89q.worldedit.util.formatting.component.TextComponentProducer;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.storage.ChunkStore; import com.sk89q.worldedit.world.storage.ChunkStore;
import com.sk89q.worldedit.util.formatting.text.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -754,19 +754,18 @@ public class SelectionCommands {
limit.ifPresent(integer -> player.print(integer + " points maximum.")); limit.ifPresent(integer -> player.print(integer + " points maximum."));
} else { } else {
CommandListBox box = new CommandListBox("Selection modes"); CommandListBox box = new CommandListBox("Selection modes");
StyledFragment contents = box.getContents(); TextComponentProducer contents = box.getContents();
StyledFragment tip = contents.createFragment(Style.RED); contents.append(SubtleFormat.wrap("Select one of the modes below:")).newline();
tip.append("Select one of the modes below:").newLine();
box.appendCommand("cuboid", "Select two corners of a cuboid"); box.appendCommand("cuboid", "Select two corners of a cuboid", "//sel cuboid");
box.appendCommand("extend", "Fast cuboid selection mode"); box.appendCommand("extend", "Fast cuboid selection mode", "//sel extend");
box.appendCommand("poly", "Select a 2D polygon with height"); box.appendCommand("poly", "Select a 2D polygon with height", "//sel poly");
box.appendCommand("ellipsoid", "Select an ellipsoid"); box.appendCommand("ellipsoid", "Select an ellipsoid", "//sel ellipsoid");
box.appendCommand("sphere", "Select a sphere"); box.appendCommand("sphere", "Select a sphere", "//sel sphere");
box.appendCommand("cyl", "Select a cylinder"); box.appendCommand("cyl", "Select a cylinder", "//sel cyl");
box.appendCommand("convex", "Select a convex polyhedral"); box.appendCommand("convex", "Select a convex polyhedral", "//sel convex");
player.printRaw(ColorCodeBuilder.asColorCodes(box)); player.print(box.create());
return; return;
} }

View File

@ -58,12 +58,15 @@ import com.sk89q.worldedit.util.command.CommandMapping;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.PrimaryAliasComparator; import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.binding.Text;
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; import com.sk89q.worldedit.util.formatting.component.CodeFormat;
import com.sk89q.worldedit.util.formatting.Style;
import com.sk89q.worldedit.util.formatting.StyledFragment;
import com.sk89q.worldedit.util.formatting.component.Code;
import com.sk89q.worldedit.util.formatting.component.CommandListBox; import com.sk89q.worldedit.util.formatting.component.CommandListBox;
import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.util.formatting.component.ErrorFormat;
import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
import com.sk89q.worldedit.util.formatting.component.TextComponentProducer;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
@ -669,17 +672,19 @@ public class UtilityCommands {
// Box // Box
CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal)); CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal));
StyledFragment contents = box.getContents(); TextComponentProducer tip = new TextComponentProducer();
StyledFragment tip = contents.createFragment(Style.GRAY); tip.getBuilder().content("").color(TextColor.GRAY);
TextComponentProducer contents = box.getContents();
if (offset >= aliases.size()) { if (offset >= aliases.size()) {
tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)).newLine(); tip.append(ErrorFormat.wrap(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal))).newline();
} else { } else {
List<CommandMapping> list = aliases.subList(offset, Math.min(offset + perPage, aliases.size())); List<CommandMapping> list = aliases.subList(offset, Math.min(offset + perPage, aliases.size()));
tip.append("Type "); tip.append("Type ");
tip.append(new Code().append("//help ").append("<command> [<page>]")); tip.append(CodeFormat.wrap("//help "));
tip.append(" for more information.").newLine(); tip.append("<command> [<page>] for more information.");
tip.newline();
// Add each command // Add each command
for (CommandMapping mapping : list) { for (CommandMapping mapping : list) {
@ -696,10 +701,11 @@ public class UtilityCommands {
} }
} }
actor.printRaw(ColorCodeBuilder.asColorCodes(box)); contents.append(tip.create());
actor.print(box.create());
} else { } else {
CommandUsageBox box = new CommandUsageBox(callable, Joiner.on(" ").join(visited)); CommandUsageBox box = new CommandUsageBox(callable, Joiner.on(" ").join(visited));
actor.printRaw(ColorCodeBuilder.asColorCodes(box)); actor.print(box.create());
} }
} }

View File

@ -23,6 +23,7 @@ import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.session.SessionOwner; import com.sk89q.worldedit.session.SessionOwner;
import com.sk89q.worldedit.util.Identifiable; import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.auth.Subject; import com.sk89q.worldedit.util.auth.Subject;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import java.io.File; import java.io.File;
@ -75,6 +76,13 @@ public interface Actor extends Identifiable, SessionOwner, Subject {
*/ */
void printError(String msg); void printError(String msg);
/**
* Print a {@link TextComponent}.
*
* @param component The component to print
*/
void print(TextComponent component);
/** /**
* Returns true if the actor can destroy bedrock. * Returns true if the actor can destroy bedrock.
* *

View File

@ -77,7 +77,6 @@ import com.sk89q.worldedit.util.command.parametric.ExceptionConverter;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler; import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler; import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat; import com.sk89q.worldedit.util.logging.LogFormat;
@ -300,7 +299,7 @@ public final class CommandManager {
actor.printError("You are not permitted to do that. Are you in the right mode?"); actor.printError("You are not permitted to do that. Are you in the right mode?");
} catch (InvalidUsageException e) { } catch (InvalidUsageException e) {
if (e.isFullHelpSuggested()) { if (e.isFullHelpSuggested()) {
actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals))); actor.print(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals).create());
String message = e.getMessage(); String message = e.getMessage();
if (message != null) { if (message != null) {
actor.printError(message); actor.printError(message);

View File

@ -34,6 +34,7 @@ import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import java.util.UUID; import java.util.UUID;
@ -132,6 +133,11 @@ class PlayerProxy extends AbstractPlayerActor {
basePlayer.printError(msg); basePlayer.printError(msg);
} }
@Override
public void print(TextComponent component) {
basePlayer.print(component);
}
@Override @Override
public String[] getGroups() { public String[] getGroups() {
return permActor.getGroups(); return permActor.getGroups();

View File

@ -1,271 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
import com.google.common.base.Joiner;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class ColorCodeBuilder {
private static final ColorCodeBuilder instance = new ColorCodeBuilder();
private static final Joiner newLineJoiner = Joiner.on("\n");
public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47;
/**
* Convert a message into color-coded text.
*
* @param message the message
* @return a list of lines
*/
public String[] build(StyledFragment message) {
StringBuilder builder = new StringBuilder();
buildFragment(builder, message, message.getStyle(), new StyleSet());
return builder.toString().split("\r?\n");
}
/**
* Build a fragment.
*
* @param builder the string builder
* @param message the message
* @param parentStyle the parent style
* @param lastStyle the last style
* @return the last style used
*/
private StyleSet buildFragment(StringBuilder builder, StyledFragment message, StyleSet parentStyle, StyleSet lastStyle) {
for (Fragment node : message.getChildren()) {
if (node instanceof StyledFragment) {
StyledFragment fragment = (StyledFragment) node;
lastStyle = buildFragment(
builder, fragment,
parentStyle.extend(message.getStyle()), lastStyle);
} else {
StyleSet style = parentStyle.extend(message.getStyle());
builder.append(getAdditive(style, lastStyle));
builder.append(node);
lastStyle = style;
}
}
return lastStyle;
}
/**
* Get the formatting codes.
*
* @param style the style
* @return the color codes
*/
public static String getFormattingCode(StyleSet style) {
StringBuilder builder = new StringBuilder();
if (style.isBold()) {
builder.append(Style.BOLD);
}
if (style.isItalic()) {
builder.append(Style.ITALIC);
}
if (style.isUnderline()) {
builder.append(Style.UNDERLINE);
}
if (style.isStrikethrough()) {
builder.append(Style.STRIKETHROUGH);
}
return builder.toString();
}
/**
* Get the formatting and color codes.
*
* @param style the style
* @return the color codes
*/
public static String getCode(StyleSet style) {
StringBuilder builder = new StringBuilder();
builder.append(getFormattingCode(style));
if (style.getColor() != null) {
builder.append(style.getColor());
}
return builder.toString();
}
/**
* Get the additional color codes needed to set the given style when the current
* style is the other given one.
*
* @param resetTo the style to reset to
* @param resetFrom the style to reset from
* @return the color codes
*/
public static String getAdditive(StyleSet resetTo, StyleSet resetFrom) {
if (!resetFrom.hasFormatting() && resetTo.hasFormatting()) {
StringBuilder builder = new StringBuilder();
builder.append(getFormattingCode(resetTo));
if (resetFrom.getColor() != resetTo.getColor()) {
builder.append(resetTo.getColor());
}
return builder.toString();
} else if (!resetFrom.hasEqualFormatting(resetTo) ||
(resetFrom.getColor() != null && resetTo.getColor() == null)) {
// Have to set reset code and add back all the formatting codes
return Style.RESET + getCode(resetTo);
} else {
if (resetFrom.getColor() != resetTo.getColor()) {
return String.valueOf(resetTo.getColor());
}
}
return "";
}
/**
* Word wrap the given text and maintain color codes throughout lines.
*
* <p>This is borrowed from Bukkit.</p>
*
* @param rawString the raw string
* @param lineLength the maximum line length
* @return a list of lines
*/
private String[] wordWrap(String rawString, int lineLength) {
// A null string is a single line
if (rawString == null) {
return new String[] {""};
}
// A string shorter than the lineWidth is a single line
if (rawString.length() <= lineLength && !rawString.contains("\n")) {
return new String[] {rawString};
}
char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination
StringBuilder word = new StringBuilder();
StringBuilder line = new StringBuilder();
List<String> lines = new LinkedList<>();
int lineColorChars = 0;
for (int i = 0; i < rawChars.length; i++) {
char c = rawChars[i];
// skip chat color modifiers
if (c == Style.COLOR_CHAR) {
word.append(Style.getByChar(rawChars[i + 1]));
lineColorChars += 2;
i++; // Eat the next character as we have already processed it
continue;
}
if (c == ' ' || c == '\n') {
if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line
String wordStr = word.toString();
String transformed;
if ((transformed = transform(wordStr)) != null) {
line.append(transformed);
} else {
lines.addAll(Arrays.asList(word.toString().split("(?<=\\G.{" + lineLength + "})")));
}
} else if (line.length() + word.length() - lineColorChars == lineLength) { // Line exactly the correct length...newline
line.append(' ');
line.append(word);
lines.add(line.toString());
line = new StringBuilder();
lineColorChars = 0;
} else if (line.length() + 1 + word.length() - lineColorChars > lineLength) { // Line too long...break the line
String wordStr = word.toString();
String transformed;
if (word.length() > lineLength && (transformed = transform(wordStr)) != null) {
if (line.length() + 1 + transformed.length() - lineColorChars > lineLength) {
lines.add(line.toString());
line = new StringBuilder(transformed);
lineColorChars = 0;
} else {
if (line.length() > 0) {
line.append(' ');
}
line.append(transformed);
}
} else {
for (String partialWord : wordStr.split("(?<=\\G.{" + lineLength + "})")) {
lines.add(line.toString());
line = new StringBuilder(partialWord);
}
lineColorChars = 0;
}
} else {
if (line.length() > 0) {
line.append(' ');
}
line.append(word);
}
word = new StringBuilder();
if (c == '\n') { // Newline forces the line to flush
lines.add(line.toString());
line = new StringBuilder();
}
} else {
word.append(c);
}
}
if(line.length() > 0) { // Only add the last line if there is anything to add
lines.add(line.toString());
}
// Iterate over the wrapped lines, applying the last color from one line to the beginning of the next
if (lines.get(0).isEmpty() || lines.get(0).charAt(0) != Style.COLOR_CHAR) {
lines.set(0, Style.WHITE + lines.get(0));
}
for (int i = 1; i < lines.size(); i++) {
final String pLine = lines.get(i-1);
final String subLine = lines.get(i);
char color = pLine.charAt(pLine.lastIndexOf(Style.COLOR_CHAR) + 1);
if (subLine.isEmpty() || subLine.charAt(0) != Style.COLOR_CHAR) {
lines.set(i, Style.getByChar(color) + subLine);
}
}
return lines.toArray(new String[lines.size()]);
}
/**
* Callback for transforming a word, such as a URL.
*
* @param word the word
* @return the transformed value, or null to do nothing
*/
protected String transform(String word) {
return null;
}
/**
* Convert the given styled fragment into color codes.
*
* @param fragment the fragment
* @return color codes
*/
public static String asColorCodes(StyledFragment fragment) {
return newLineJoiner.join(instance.build(fragment));
}
}

View File

@ -1,92 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
/**
* A fragment of text.
*/
public class Fragment {
private final StringBuilder builder = new StringBuilder();
Fragment() {
}
public Fragment append(String str) {
builder.append(Style.stripColor(str));
return this;
}
public Fragment append(Object obj) {
append(String.valueOf(obj));
return this;
}
public Fragment append(StringBuffer sb) {
append(String.valueOf(sb));
return this;
}
public Fragment append(CharSequence s) {
append(String.valueOf(s));
return this;
}
public Fragment append(boolean b) {
append(String.valueOf(b));
return this;
}
public Fragment append(char c) {
append(String.valueOf(c));
return this;
}
public Fragment append(int i) {
append(String.valueOf(i));
return this;
}
public Fragment append(long lng) {
append(String.valueOf(lng));
return this;
}
public Fragment append(float f) {
append(String.valueOf(f));
return this;
}
public Fragment append(double d) {
append(String.valueOf(d));
return this;
}
public Fragment newLine() {
append("\n");
return this;
}
@Override
public String toString() {
return builder.toString();
}
}

View File

@ -1,276 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.regex.Pattern;
/**
* All supported color values for chat.
*
* <p>From Bukkit.</p>
*/
public enum Style {
/**
* Represents black
*/
BLACK('0', 0x00),
/**
* Represents dark blue
*/
BLUE_DARK('1', 0x1),
/**
* Represents dark green
*/
GREEN_DARK('2', 0x2),
/**
* Represents dark blue (aqua)
*/
CYAN_DARK('3', 0x3),
/**
* Represents dark red
*/
RED_DARK('4', 0x4),
/**
* Represents dark purple
*/
PURPLE_DARK('5', 0x5),
/**
* Represents gold
*/
YELLOW_DARK('6', 0x6),
/**
* Represents gray
*/
GRAY('7', 0x7),
/**
* Represents dark gray
*/
GRAY_DARK('8', 0x8),
/**
* Represents blue
*/
BLUE('9', 0x9),
/**
* Represents green
*/
GREEN('a', 0xA),
/**
* Represents aqua
*/
CYAN('b', 0xB),
/**
* Represents red
*/
RED('c', 0xC),
/**
* Represents light purple
*/
PURPLE('d', 0xD),
/**
* Represents yellow
*/
YELLOW('e', 0xE),
/**
* Represents white
*/
WHITE('f', 0xF),
/**
* Represents magical characters that change around randomly
*/
RANDOMIZE('k', 0x10, true),
/**
* Makes the text bold.
*/
BOLD('l', 0x11, true),
/**
* Makes a line appear through the text.
*/
STRIKETHROUGH('m', 0x12, true),
/**
* Makes the text appear underlined.
*/
UNDERLINE('n', 0x13, true),
/**
* Makes the text italic.
*/
ITALIC('o', 0x14, true),
/**
* Resets all previous chat colors or formats.
*/
RESET('r', 0x15);
/**
* The special character which prefixes all chat color codes. Use this if you need to dynamically
* convert color codes from your custom format.
*/
public static final char COLOR_CHAR = '\u00A7';
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]");
private final int intCode;
private final char code;
private final boolean isFormat;
private final String toString;
private final static Map<Integer, Style> BY_ID = Maps.newHashMap();
private final static Map<Character, Style> BY_CHAR = Maps.newHashMap();
Style(char code, int intCode) {
this(code, intCode, false);
}
Style(char code, int intCode, boolean isFormat) {
this.code = code;
this.intCode = intCode;
this.isFormat = isFormat;
this.toString = new String(new char[] {COLOR_CHAR, code});
}
/**
* Gets the char value associated with this color
*
* @return A char value of this color code
*/
public char getChar() {
return code;
}
@Override
public String toString() {
return toString;
}
/**
* Checks if this code is a format code as opposed to a color code.
*
* @return the if the code is a formatting code
*/
public boolean isFormat() {
return isFormat;
}
/**
* Checks if this code is a color code as opposed to a format code.
*
* @return the if the code is a color
*/
public boolean isColor() {
return !isFormat && this != RESET;
}
/**
* Gets the color represented by the specified color code
*
* @param code Code to check
* @return Associative Style with the given code, or null if it doesn't exist
*/
public static Style getByChar(char code) {
return BY_CHAR.get(code);
}
/**
* Gets the color represented by the specified color code
*
* @param code Code to check
* @return Associative Style with the given code, or null if it doesn't exist
*/
public static Style getByChar(String code) {
checkNotNull(code);
checkArgument(!code.isEmpty(), "Code must have at least one character");
return BY_CHAR.get(code.charAt(0));
}
/**
* Strips the given message of all color codes
*
* @param input String to strip of color
* @return A copy of the input string, without any coloring
*/
public static String stripColor(final String input) {
if (input == null) {
return null;
}
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
}
/**
* Translates a string using an alternate color code character into a string that uses the internal
* ChatColor.COLOR_CODE color code character. The alternate color code character will only be replaced
* if it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r.
*
* @param altColorChar The alternate color code character to replace. Ex: &amp;
* @param textToTranslate Text containing the alternate color code character.
* @return Text containing the ChatColor.COLOR_CODE color code character.
*/
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
char[] b = textToTranslate.toCharArray();
for (int i = 0; i < b.length - 1; i++) {
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) {
b[i] = Style.COLOR_CHAR;
b[i+1] = Character.toLowerCase(b[i+1]);
}
}
return new String(b);
}
/**
* Gets the ChatColors used at the end of the given input string.
*
* @param input Input string to retrieve the colors from.
* @return Any remaining ChatColors to pass onto the next line.
*/
public static String getLastColors(String input) {
String result = "";
int length = input.length();
// Search backwards from the end as it is faster
for (int index = length - 1; index > -1; index--) {
char section = input.charAt(index);
if (section == COLOR_CHAR && index < length - 1) {
char c = input.charAt(index + 1);
Style color = getByChar(c);
if (color != null) {
result = color + result;
// Once we find a color or reset we can stop searching
if (color.isColor() || color == RESET) {
break;
}
}
}
}
return result;
}
static {
for (Style color : values()) {
BY_ID.put(color.intCode, color);
BY_CHAR.put(color.code, color);
}
}
}

View File

@ -1,249 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
/**
* Represents set of styles, such as color, bold, etc.
*/
public class StyleSet {
private Boolean bold;
private Boolean italic;
private Boolean underline;
private Boolean strikethrough;
private Style color;
/**
* Create a new style set with no properties set.
*/
public StyleSet() {
}
/**
* Create a new style set with the given styles.
*
* <p>{@link Style#RESET} will be ignored if provided.</p>
*
* @param styles a list of styles
*/
public StyleSet(Style... styles) {
for (Style style : styles) {
if (style.isColor()) {
color = style;
} else if (style == Style.BOLD) {
bold = true;
} else if (style == Style.ITALIC) {
italic = true;
} else if (style == Style.UNDERLINE) {
underline = true;
} else if (style == Style.STRIKETHROUGH) {
strikethrough = true;
}
}
}
/**
* Get whether this style set is bold.
*
* @return true, false, or null if unset
*/
public Boolean getBold() {
return bold;
}
/**
* Get whether the text is bold.
*
* @return true if bold
*/
public boolean isBold() {
return getBold() != null && getBold();
}
/**
* Set whether the text is bold.
*
* @param bold true, false, or null to unset
*/
public void setBold(Boolean bold) {
this.bold = bold;
}
/**
* Get whether this style set is italicized.
*
* @return true, false, or null if unset
*/
public Boolean getItalic() {
return italic;
}
/**
* Get whether the text is italicized.
*
* @return true if italicized
*/
public boolean isItalic() {
return getItalic() != null && getItalic();
}
/**
* Set whether the text is italicized.
*
* @param italic false, or null to unset
*/
public void setItalic(Boolean italic) {
this.italic = italic;
}
/**
* Get whether this style set is underlined.
*
* @return true, false, or null if unset
*/
public Boolean getUnderline() {
return underline;
}
/**
* Get whether the text is underlined.
*
* @return true if underlined
*/
public boolean isUnderline() {
return getUnderline() != null && getUnderline();
}
/**
* Set whether the text is underline.
*
* @param underline false, or null to unset
*/
public void setUnderline(Boolean underline) {
this.underline = underline;
}
/**
* Get whether this style set is stricken through.
*
* @return true, false, or null if unset
*/
public Boolean getStrikethrough() {
return strikethrough;
}
/**
* Get whether the text is stricken through.
*
* @return true if there is strikethrough applied
*/
public boolean isStrikethrough() {
return getStrikethrough() != null && getStrikethrough();
}
/**
* Set whether the text is stricken through.
*
* @param strikethrough false, or null to unset
*/
public void setStrikethrough(Boolean strikethrough) {
this.strikethrough = strikethrough;
}
/**
* Get the color of the text.
*
* @return true, false, or null if unset
*/
public Style getColor() {
return color;
}
/**
* Set the color of the text.
*
* @param color the color
*/
public void setColor(Style color) {
this.color = color;
}
/**
* Return whether text formatting (bold, italics, underline, strikethrough) is set.
*
* @return true if formatting is set
*/
public boolean hasFormatting() {
return getBold() != null || getItalic() != null
|| getUnderline() != null || getStrikethrough() != null;
}
/**
* Return where the text formatting of the given style set is different from
* that assigned to this one.
*
* @param other the other style set
* @return true if there is a difference
*/
public boolean hasEqualFormatting(StyleSet other) {
return getBold() == other.getBold() && getItalic() == other.getItalic()
&& getUnderline() == other.getUnderline() &&
getStrikethrough() == other.getStrikethrough();
}
/**
* Create a new instance with styles inherited from this one but with new styles
* from the given style set.
*
* @param style the style set
* @return a new style set instance
*/
public StyleSet extend(StyleSet style) {
StyleSet newStyle = clone();
if (style.getBold() != null) {
newStyle.setBold(style.getBold());
}
if (style.getItalic() != null) {
newStyle.setItalic(style.getItalic());
}
if (style.getUnderline() != null) {
newStyle.setUnderline(style.getUnderline());
}
if (style.getStrikethrough() != null) {
newStyle.setStrikethrough(style.getStrikethrough());
}
if (style.getColor() != null) {
newStyle.setColor(style.getColor());
}
return newStyle;
}
@Override
public StyleSet clone() {
StyleSet style = new StyleSet();
style.setBold(getBold());
style.setItalic(getItalic());
style.setUnderline(getUnderline());
style.setStrikethrough(getStrikethrough());
style.setColor(getColor());
return style;
}
}

View File

@ -1,150 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting;
import java.util.ArrayList;
import java.util.List;
/**
* A fragment of text that can be styled.
*/
public class StyledFragment extends Fragment {
private final List<Fragment> children = new ArrayList<>();
private StyleSet style;
private Fragment lastText;
public StyledFragment() {
style = new StyleSet();
}
public StyledFragment(StyleSet style) {
this.style = style;
}
public StyledFragment(Style... styles) {
this.style = new StyleSet(styles);
}
public StyleSet getStyle() {
return style;
}
public void setStyles(StyleSet style) {
this.style = style;
}
public List<Fragment> getChildren() {
return children;
}
protected Fragment lastText() {
Fragment text;
if (!children.isEmpty()) {
text = children.get(children.size() - 1);
if (text == lastText) {
return text;
}
}
text = new Fragment();
this.lastText = text;
children.add(text);
return text;
}
public StyledFragment createFragment(Style... styles) {
StyledFragment fragment = new StyledFragment(styles);
append(fragment);
return fragment;
}
public StyledFragment append(StyledFragment fragment) {
children.add(fragment);
return this;
}
@Override
public StyledFragment append(String str) {
lastText().append(str);
return this;
}
@Override
public StyledFragment append(Object obj) {
append(String.valueOf(obj));
return this;
}
@Override
public StyledFragment append(StringBuffer sb) {
append(String.valueOf(sb));
return this;
}
@Override
public StyledFragment append(CharSequence s) {
append(String.valueOf(s));
return this;
}
@Override
public StyledFragment append(boolean b) {
append(String.valueOf(b));
return this;
}
@Override
public StyledFragment append(char c) {
append(String.valueOf(c));
return this;
}
@Override
public StyledFragment append(int i) {
append(String.valueOf(i));
return this;
}
@Override
public StyledFragment append(long lng) {
append(String.valueOf(lng));
return this;
}
@Override
public StyledFragment append(float f) {
append(String.valueOf(f));
return this;
}
@Override
public StyledFragment append(double d) {
append(String.valueOf(d));
return this;
}
@Override
public StyledFragment newLine() {
append("\n");
return this;
}
}

View File

@ -19,19 +19,30 @@
package com.sk89q.worldedit.util.formatting.component; package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.text.format.TextColor;
/** /**
* Represents a fragment representing a command that is to be typed. * Represents a fragment representing a command that is to be typed.
*/ */
public class Code extends StyledFragment { public class CodeFormat extends TextComponentProducer {
/** private CodeFormat() {
* Create a new instance. getBuilder().content("").color(TextColor.AQUA);
*/
public Code() {
super(Style.CYAN);
} }
/**
* Creates a CodeFormat with the given message.
*
* @param texts The text
* @return The Component
*/
public static TextComponent wrap(String ... texts) {
CodeFormat code = new CodeFormat();
for (String text: texts) {
code.append(text);
}
return code.create();
}
} }

View File

@ -19,7 +19,10 @@
package com.sk89q.worldedit.util.formatting.component; package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
public class CommandListBox extends MessageBox { public class CommandListBox extends MessageBox {
@ -31,14 +34,24 @@ public class CommandListBox extends MessageBox {
* @param title the title * @param title the title
*/ */
public CommandListBox(String title) { public CommandListBox(String title) {
super(title); super(title, new TextComponentProducer());
} }
public CommandListBox appendCommand(String alias, String description) { public CommandListBox appendCommand(String alias, String description) {
if (!first) { return appendCommand(alias, description, null);
getContents().newLine();
} }
getContents().createFragment(Style.YELLOW_DARK).append(alias).append(": ");
public CommandListBox appendCommand(String alias, String description, String insertion) {
if (!first) {
getContents().newline();
}
TextComponent commandName = TextComponent.of(alias, TextColor.GOLD);
if (insertion != null) {
commandName = commandName
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, insertion))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to select")));
}
getContents().append(commandName.append(TextComponent.of(": ")));
getContents().append(description); getContents().append(description);
first = false; first = false;
return this; return this;

View File

@ -28,7 +28,7 @@ import com.sk89q.worldedit.util.command.CommandMapping;
import com.sk89q.worldedit.util.command.Description; import com.sk89q.worldedit.util.command.Description;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.PrimaryAliasComparator; import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.text.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -38,7 +38,7 @@ import javax.annotation.Nullable;
/** /**
* A box to describe usage of a command. * A box to describe usage of a command.
*/ */
public class CommandUsageBox extends StyledFragment { public class CommandUsageBox extends TextComponentProducer {
/** /**
* Create a new usage box. * Create a new usage box.
@ -80,31 +80,31 @@ public class CommandUsageBox extends StyledFragment {
} }
} }
append(box); append(box.create());
} }
private void attachCommandUsage(Description description, String commandString) { private void attachCommandUsage(Description description, String commandString) {
MessageBox box = new MessageBox("Help for " + commandString); TextComponentProducer contents = new TextComponentProducer();
StyledFragment contents = box.getContents();
if (description.getUsage() != null) { if (description.getUsage() != null) {
contents.append(new Label().append("Usage: ")); contents.append(LabelFormat.wrap("Usage: "));
contents.append(description.getUsage()); contents.append(description.getUsage());
} else { } else {
contents.append(new Subtle().append("Usage information is not available.")); contents.append(SubtleFormat.wrap("Usage information is not available."));
} }
contents.newLine(); contents.append(Component.newline());
if (description.getHelp() != null) { if (description.getHelp() != null) {
contents.append(description.getHelp()); contents.append(description.getHelp());
} else if (description.getDescription() != null) { } else if (description.getDescription() != null) {
contents.append(description.getDescription()); contents.append(description.getDescription());
} else { } else {
contents.append(new Subtle().append("No further help is available.")); contents.append(SubtleFormat.wrap("No further help is available."));
} }
append(box); MessageBox box = new MessageBox("Help for " + commandString, contents);
append(box.create());
} }
} }

View File

@ -0,0 +1,51 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
/**
* Represents a fragment representing an error.
*/
public class ErrorFormat extends TextComponentProducer {
/**
* Create a new instance.
*/
private ErrorFormat() {
getBuilder().content("").color(TextColor.RED);
}
/**
* Creates an ErrorFormat with the given message.
*
* @param texts The text
* @return The Component
*/
public static TextComponent wrap(String ... texts) {
ErrorFormat error = new ErrorFormat();
for (String component : texts) {
error.append(component);
}
return error.create();
}
}

View File

@ -19,19 +19,33 @@
package com.sk89q.worldedit.util.formatting.component; package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.text.format.TextColor;
/** /**
* Represents a fragment representing a label. * Represents a fragment representing a label.
*/ */
public class Label extends StyledFragment { public class LabelFormat extends TextComponentProducer {
/** /**
* Create a new instance. * Create a new instance.
*/ */
public Label() { private LabelFormat() {
super(Style.YELLOW); getBuilder().content("").color(TextColor.YELLOW);
} }
/**
* Creates a LabelFormat with the given message.
*
* @param texts The text
* @return The Component
*/
public static TextComponent wrap(String ... texts) {
LabelFormat label = new LabelFormat();
for (String component : texts) {
label.append(component);
}
return label.create();
}
} }

View File

@ -21,37 +21,39 @@ package com.sk89q.worldedit.util.formatting.component;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.text.format.TextColor;
/** /**
* Makes for a box with a border above and below. * Makes for a box with a border above and below.
*/ */
public class MessageBox extends StyledFragment { public class MessageBox extends TextComponentProducer {
private final StyledFragment contents = new StyledFragment(); private static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47;
private final TextComponentProducer contents;
/** /**
* Create a new box. * Create a new box.
*/ */
public MessageBox(String title) { public MessageBox(String title, TextComponentProducer contents) {
checkNotNull(title); checkNotNull(title);
int leftOver = ColorCodeBuilder.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - title.length() - 2; int leftOver = GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - title.length() - 2;
int leftSide = (int) Math.floor(leftOver * 1.0/3); int leftSide = (int) Math.floor(leftOver * 1.0/3);
int rightSide = (int) Math.floor(leftOver * 2.0/3); int rightSide = (int) Math.floor(leftOver * 2.0/3);
if (leftSide > 0) { if (leftSide > 0) {
createFragment(Style.YELLOW).append(createBorder(leftSide)); append(TextComponent.of(createBorder(leftSide), TextColor.YELLOW));
} }
append(" "); append(Component.space());
append(title); append(title);
append(" "); append(Component.space());
if (rightSide > 0) { if (rightSide > 0) {
createFragment(Style.YELLOW).append(createBorder(rightSide)); append(TextComponent.of(createBorder(rightSide), TextColor.YELLOW));
} }
newLine(); newline();
append(contents); this.contents = contents;
} }
private String createBorder(int count) { private String createBorder(int count) {
@ -63,12 +65,17 @@ public class MessageBox extends StyledFragment {
} }
/** /**
* Get the internal contents. * Gets the message box contents producer.
* *
* @return the contents * @return The contents producer
*/ */
public StyledFragment getContents() { public TextComponentProducer getContents() {
return contents; return contents;
} }
@Override
public TextComponent create() {
append(contents.create());
return super.create();
}
} }

View File

@ -19,19 +19,33 @@
package com.sk89q.worldedit.util.formatting.component; package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.Style; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.text.format.TextColor;
/** /**
* Represents a subtle part of the message. * Represents a subtle part of the message.
*/ */
public class Subtle extends StyledFragment { public class SubtleFormat extends TextComponentProducer {
/** /**
* Create a new instance. * Create a new instance.
*/ */
public Subtle() { private SubtleFormat() {
super(Style.GRAY); getBuilder().content("").color(TextColor.GRAY);
} }
/**
* Creates a SubtleFormat with the given message.
*
* @param texts The text
* @return The Component
*/
public static TextComponent wrap(String ... texts) {
SubtleFormat subtle = new SubtleFormat();
for (String component : texts) {
subtle.append(component);
}
return subtle.create();
}
} }

View File

@ -0,0 +1,68 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.formatting.component;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
public class TextComponentProducer {
private TextComponent.Builder builder = TextComponent.builder().content("");
public TextComponent.Builder getBuilder() {
return builder;
}
/**
* Adds a component as a child to this Producer.
*
* @param component The component
* @return The producer, for chaining
*/
public TextComponentProducer append(Component component) {
getBuilder().append(component);
return this;
}
/**
* Adds a string as a child to this Producer.
*
* @param string The text
* @return The producer, for chaining
*/
public TextComponentProducer append(String string) {
getBuilder().append(TextComponent.of(string));
return this;
}
/**
* Adds a newline as a child to this Producer.
*
* @return The producer, for chaining
*/
public TextComponentProducer newline() {
getBuilder().append(Component.newline());
return this;
}
public TextComponent create() {
return builder.build();
}
}

View File

@ -87,7 +87,6 @@ shadowJar {
relocate "org.slf4j", "com.sk89q.worldedit.slf4j" relocate "org.slf4j", "com.sk89q.worldedit.slf4j"
relocate "org.apache.logging.slf4j", "com.sk89q.worldedit.log4jbridge" relocate "org.apache.logging.slf4j", "com.sk89q.worldedit.log4jbridge"
include(dependency(':worldedit-core'))
include(dependency('org.slf4j:slf4j-api')) include(dependency('org.slf4j:slf4j-api'))
include(dependency("org.apache.logging.log4j:log4j-slf4j-impl")) include(dependency("org.apache.logging.log4j:log4j-slf4j-impl"))
} }

View File

@ -31,9 +31,11 @@ import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import io.netty.buffer.Unpooled;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
@ -42,6 +44,7 @@ import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TextFormatting;
@ -49,8 +52,6 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import io.netty.buffer.Unpooled;
public class ForgePlayer extends AbstractPlayerActor { public class ForgePlayer extends AbstractPlayerActor {
private final EntityPlayerMP player; private final EntityPlayerMP player;
@ -141,6 +142,11 @@ public class ForgePlayer extends AbstractPlayerActor {
sendColorized(msg, TextFormatting.RED); sendColorized(msg, TextFormatting.RED);
} }
@Override
public void print(TextComponent component) {
this.player.sendMessage(ITextComponent.Serializer.fromJson(GsonComponentSerializer.INSTANCE.serialize(component)));
}
private void sendColorized(String msg, TextFormatting formatting) { private void sendColorized(String msg, TextFormatting formatting) {
for (String part : msg.split("\n")) { for (String part : msg.split("\n")) {
TextComponentString component = new TextComponentString(part); TextComponentString component = new TextComponentString(part);

116
worldedit-libs/build.gradle Normal file
View File

@ -0,0 +1,116 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
/*
This project shades <em>API</em> libraries, i.e. those libraries
whose classes are publicly referenced from `-core` classes.
This project <em>does not</em> shade implementation libraries, i.e.
those libraries whose classes are internally depended on.
This is because the main reason for shading those libraries is for
their internal usage in each platform, not because we need them available to
dependents of `-core` to compile and work with WorldEdit's API.
*/
subprojects {
apply plugin: 'maven'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.jfrog.artifactory'
configurations {
create("shade")
getByName("archives").extendsFrom(getByName("default"))
}
group = rootProject.group + ".worldedit-libs"
tasks.register("jar", ShadowJar) {
configurations = [project.configurations.shade]
classifier = ""
dependencies {
exclude(dependency("com.google.guava:guava"))
exclude(dependency("com.google.code.gson:gson"))
exclude(dependency("org.checkerframework:checker-qual"))
}
relocate('net.kyori.text', 'com.sk89q.worldedit.util.formatting.text')
}
def altConfigFiles = { String artifactType ->
def deps = configurations.shade.incoming.dependencies
.collect { it.copy() }
.collect { dependency ->
dependency.artifact { artifact ->
artifact.name = dependency.name
artifact.type = artifactType
artifact.extension = 'jar'
artifact.classifier = artifactType
}
dependency
}
return files(configurations.detachedConfiguration(deps as Dependency[])
.resolvedConfiguration.lenientConfiguration.getArtifacts()
.findAll { it.classifier == artifactType }
.collect { zipTree(it.file) })
}
tasks.register("sourcesJar", Jar) {
from {
altConfigFiles('sources')
}
def filePattern = ~'(.*)net/kyori/text((?:/|$).*)'
def textPattern = ~/net\.kyori\.text/
eachFile {
it.filter { String line ->
line.replaceFirst(textPattern, 'com.sk89q.worldedit.util.formatting.text')
}
it.path = it.path.replaceFirst(filePattern, '$1com/sk89q/worldedit/util/formatting/text$2')
}
classifier = "sources"
}
artifacts {
add("default", jar)
add("archives", sourcesJar)
}
tasks.register("install", Upload) {
configuration = configurations.archives
repositories.mavenInstaller {
pom.version = project.version
pom.artifactId = project.name
}
}
artifactoryPublish {
publishConfigs('default')
}
build.dependsOn(jar, sourcesJar)
}
project("core") {
dependencies {
shade 'net.kyori:text-api:2.0.0'
shade 'net.kyori:text-serializer-gson:2.0.0'
shade 'net.kyori:text-serializer-legacy:2.0.0'
shade('com.sk89q:jchronic:0.2.4a') {
exclude(group: "junit", module: "junit")
}
shade 'com.thoughtworks.paranamer:paranamer:2.6'
shade 'com.sk89q.lib:jlibnoise:1.0.0'
}
}
project("bukkit") {
dependencies {
shade 'net.kyori:text-adapter-bukkit:2.0.0-SNAPSHOT'
}
}
project("sponge") {
dependencies {
shade 'net.kyori:text-adapter-spongeapi:2.0.0-SNAPSHOT'
}
}
tasks.register("build") {
dependsOn(subprojects.collect { it.tasks.named("build") })
}

View File

@ -17,6 +17,7 @@ repositories {
dependencies { dependencies {
compile project(':worldedit-core') compile project(':worldedit-core')
compile project(':worldedit-libs:sponge')
compile 'org.spongepowered:spongeapi:7.1.0' compile 'org.spongepowered:spongeapi:7.1.0'
compile 'org.bstats:bstats-sponge:1.4' compile 'org.bstats:bstats-sponge:1.4'
testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1' testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1'
@ -40,13 +41,10 @@ jar {
shadowJar { shadowJar {
dependencies { dependencies {
include(dependency(':worldedit-core')) relocate ("org.bstats", "com.sk89q.worldedit.sponge.bstats") {
include(dependency('org.bstats:bstats-sponge:1.4')) include(dependency('org.bstats:bstats-sponge:1.4'))
} }
} }
artifacts {
archives shadowJar
} }
if (project.hasProperty("signing")) { if (project.hasProperty("signing")) {

View File

@ -26,6 +26,8 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text; import org.spongepowered.api.text.Text;
@ -89,6 +91,11 @@ public class SpongeCommandSender implements Actor {
sendColorized(msg, TextColors.RED); sendColorized(msg, TextColors.RED);
} }
@Override
public void print(TextComponent component) {
TextAdapter.sendComponent(sender, component);
}
private void sendColorized(String msg, TextColor formatting) { private void sendColorized(String msg, TextColor formatting) {
for (String part : msg.split("\n")) { for (String part : msg.split("\n")) {
sender.sendMessage(Text.of(formatting, TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part))); sender.sendMessage(Text.of(formatting, TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part)));

View File

@ -36,6 +36,8 @@ import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.gamemode.GameModes;
import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
import org.spongepowered.api.data.type.HandTypes; import org.spongepowered.api.data.type.HandTypes;
import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.living.player.Player;
@ -149,6 +151,11 @@ public class SpongePlayer extends AbstractPlayerActor {
sendColorized(msg, TextColors.RED); sendColorized(msg, TextColors.RED);
} }
@Override
public void print(TextComponent component) {
TextAdapter.sendComponent(player, component);
}
private void sendColorized(String msg, TextColor formatting) { private void sendColorized(String msg, TextColor formatting) {
for (String part : msg.split("\n")) { for (String part : msg.split("\n")) {
this.player.sendMessage(Text.of(formatting, TextSerializers.FORMATTING_CODE.deserialize(part))); this.player.sendMessage(Text.of(formatting, TextSerializers.FORMATTING_CODE.deserialize(part)));