001/* 002 * PermissionsEx 003 * Copyright (C) zml and PermissionsEx contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package ca.stellardrift.permissionsex.minecraft.command; 018 019import ca.stellardrift.permissionsex.context.ContextDefinitionProvider; 020import ca.stellardrift.permissionsex.minecraft.MinecraftPermissionsEx; 021import ca.stellardrift.permissionsex.subject.CalculatedSubject; 022import ca.stellardrift.permissionsex.subject.SubjectRef; 023import ca.stellardrift.permissionsex.subject.SubjectTypeCollection; 024import net.kyori.adventure.text.BuildableComponent; 025import net.kyori.adventure.text.Component; 026import net.kyori.adventure.text.ComponentBuilder; 027import net.kyori.adventure.text.ComponentLike; 028import net.kyori.adventure.text.event.ClickEvent; 029import net.kyori.adventure.text.event.HoverEvent; 030import net.kyori.adventure.text.format.NamedTextColor; 031import net.kyori.adventure.text.format.Style; 032import net.kyori.adventure.text.format.TextColor; 033import net.kyori.adventure.text.format.TextDecoration; 034import org.checkerframework.checker.nullness.qual.Nullable; 035 036import java.util.concurrent.ExecutionException; 037 038import static net.kyori.adventure.text.Component.space; 039import static net.kyori.adventure.text.Component.text; 040 041/** 042 * Message formatting parameters for the current engine instance. 043 */ 044public class MessageFormatter { 045 public static final TextColor DEFAULT_MESSAGE_COLOR = NamedTextColor.DARK_AQUA; 046 public static final TextColor DEFAULT_HIGHLIGHT_COLOR = TextColor.color(0x55cccc); 047 public static final Component EQUALS_SIGN = text("=", NamedTextColor.GRAY); 048 public static final Component SLASH = text("/"); 049 050 private final MinecraftPermissionsEx<?> manager; 051 private final TextColor highlightColor; 052 private final TextColor messageColor; 053 054 /** 055 * Create a new formatter with default message and highlight colours. 056 * 057 * @param manager the permissions manager 058 */ 059 public MessageFormatter(final MinecraftPermissionsEx<?> manager) { 060 this(manager, DEFAULT_MESSAGE_COLOR, DEFAULT_HIGHLIGHT_COLOR); 061 } 062 063 public MessageFormatter( 064 final MinecraftPermissionsEx<?> manager, 065 final TextColor messageColor, 066 final TextColor highlightColor) { 067 this.manager = manager; 068 this.messageColor = messageColor; 069 this.highlightColor = highlightColor; 070 } 071 072 final MinecraftPermissionsEx<?> manager() { 073 return this.manager; 074 } 075 076 /** 077 * Get the colour to be used for standard command responses. 078 * 079 * @return the response color 080 */ 081 public final TextColor responseColor() { 082 return this.messageColor; 083 } 084 085 /** 086 * The color to be used for highlighted parts of messages. 087 * 088 * @return the highlight color 089 */ 090 public final TextColor highlightColor() { 091 return this.highlightColor; 092 } 093 094 /** 095 * Given a command in standard format, correct it to refer to specifically the proxy format. 096 * 097 * @param cmd the original command 098 * @return the transformed command 099 */ 100 protected String transformCommand(final String cmd) { 101 return cmd; 102 } 103 104 protected <I> @Nullable String friendlyName(final SubjectRef<I> reference) { 105 return null; 106 } 107 108 public final Component subject(final CalculatedSubject subject) { 109 return this.subject(subject.identifier()); 110 } 111 112 /** 113 * Print the subject in a user-friendly manner. May link to the subject info printout 114 * 115 * @param subject The subject to show 116 * @return the formatted value 117 */ 118 public <I> Component subject(final SubjectRef<I> subject) { 119 final SubjectTypeCollection<I> type = this.manager.engine().subjects(subject.type()); 120 final String serializedIdent = subject.type().serializeIdentifier(subject.identifier()); 121 @Nullable String name = this.friendlyName(subject); 122 if (name == null) { 123 try { 124 name = type.persistentData().data(subject.identifier(), null).get() 125 .segment(ContextDefinitionProvider.GLOBAL_CONTEXT).options().get("name"); 126 } catch (final ExecutionException | InterruptedException ex) { 127 throw new RuntimeException(ex); 128 } 129 } 130 131 final Component nameText; 132 if (name != null) { 133 nameText = text() 134 .append(text(serializedIdent, NamedTextColor.GRAY)) 135 .append(SLASH) 136 .append(text(name)) 137 .build(); 138 } else { 139 nameText = text(serializedIdent); 140 } 141 142 return text() 143 .append(text(subject.type().name()).decorate(TextDecoration.BOLD)) 144 .append(space()) 145 .append(nameText) 146 .hoverEvent(HoverEvent.showText(Messages.FORMATTER_BUTTON_INFO_PROMPT.tr())) 147 .clickEvent(ClickEvent.runCommand(transformCommand("/pex " + subject.type().name() + ' ' + serializedIdent + " info"))) 148 .build(); 149 } 150 151 /** 152 * Create a clickable button that will execute a command or suggest a command to be executed 153 * 154 * @param type The style of button to present 155 * @param tooltip A tooltip to optionally show when hovering over a button 156 * @param command The command to execute 157 * @param execute Whether the command provided will be executed or only added to the user's input 158 * @return the formatted text 159 */ 160 public <C extends BuildableComponent<C, B>, B extends ComponentBuilder<C, B>> Component button( 161 B builder, 162 ButtonType type, 163 final @Nullable ComponentLike tooltip, 164 final String command, 165 final boolean execute 166 ) { 167 final TextColor buttonColor; 168 switch (type) { 169 case POSITIVE: 170 buttonColor = NamedTextColor.GREEN; 171 break; 172 case NEGATIVE: 173 buttonColor = NamedTextColor.RED; 174 break; 175 default: 176 buttonColor = this.highlightColor; 177 } 178 builder.color(buttonColor); 179 180 if (tooltip != null) { 181 builder.hoverEvent(HoverEvent.showText(tooltip)); 182 } 183 184 if (execute) { 185 builder.clickEvent(ClickEvent.runCommand(transformCommand(command))); 186 } else { 187 builder.clickEvent(ClickEvent.suggestCommand(transformCommand(command))); 188 } 189 return builder.build(); 190 } 191 192 193 public final <C extends BuildableComponent<C, B>, B extends ComponentBuilder<C, B>> B header(final B builder) { 194 return builder.decoration(TextDecoration.BOLD, true); 195 } 196 197 public final <C extends BuildableComponent<C, B>, B extends ComponentBuilder<C, B>> B hl(final B builder) { 198 return builder.color(this.highlightColor); 199 } 200 201 public final Style.Builder header(final Style.Builder builder) { 202 return builder.decoration(TextDecoration.BOLD, true); 203 } 204 205 public final Style.Builder hl(final Style.Builder builder) { 206 return builder.color(this.highlightColor); 207 } 208}