feat: implemented browser + editors
This commit is contained in:
parent
36eb128e85
commit
41724927b6
@ -6,12 +6,17 @@ import net.minestom.server.entity.GameMode;
|
||||
import net.minestom.server.event.player.PlayerChatEvent;
|
||||
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
||||
import ru.dragonestia.msb3.api.module.MotdModule;
|
||||
import ru.dragonestia.msb3.api.module.PrometheusMetricsModule;
|
||||
import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule;
|
||||
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||
import ru.dragonestia.msb3.api.talk.dialogue.DialogueRenderer;
|
||||
import ru.dragonestia.msb3.api.talk.dialogue.DialogueTheme;
|
||||
import ru.dragonestia.msb3.api.ui.PictureBanner;
|
||||
import ru.dragonestia.msb3.resource.utils.ClassPreLoader;
|
||||
import team.unnamed.creative.ResourcePack;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
@Log4j2
|
||||
public class DefaultBootstrap extends ServerInitializer {
|
||||
|
||||
@ -23,56 +28,58 @@ public class DefaultBootstrap extends ServerInitializer {
|
||||
public void onDefaultModulesLoaded() {
|
||||
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
|
||||
FlatWorldModule.init(GameMode.ADVENTURE);
|
||||
PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500));
|
||||
|
||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
|
||||
var player = event.getPlayer();
|
||||
|
||||
var render = new DialogueRenderer(player, DialogueTheme.builder().build());
|
||||
render.setText("""
|
||||
Абсолютно точно.
|
||||
Я знаю точнo - невозможное возможно
|
||||
Сойти с ума, влюбиться так неосторoжно
|
||||
Найти тебя, не отпускать ни днём, ни ночью
|
||||
Всё невозможное - возможно, знаю точно!
|
||||
А где тебя искать, прошу ты мне ответь
|
||||
В какие города мне за тобой лететь
|
||||
Я готов на край Земли, я всё должен объяснить
|
||||
Пойми, что без тебя я не умею жить
|
||||
Я знаю точно - невозможное возможно
|
||||
Сойти с ума, влюбиться так неосторожно
|
||||
Найти тебя, не отпускать ни днём, ни ночью
|
||||
Всё невозможное - возможно, знаю точно!
|
||||
На-на-на-на (на-на-на-на), а-а, а-а
|
||||
На-на-на-на (на-на-на-на), а-а, а-а
|
||||
Всё готов делить, с тобой я пополам
|
||||
Ты только мне поверь, я сделал выбор сам
|
||||
Дай же мне последний шанс, я всё должен объяснить
|
||||
Пойми, что без тебя я не умею жить
|
||||
Я знаю точно - невозможное возможно
|
||||
Сойти с ума, влюбиться так неосторожно
|
||||
Найти тебя, не отпускать ни днём, ни ночью
|
||||
Всё невозможное - возможно, знаю точно!
|
||||
На-на-на-на (на-на-на-на), а-а, а-а
|
||||
На-на-на-на (на-на-на-на), а-а, а-а
|
||||
Я знаю точно - невозможное возможно
|
||||
Сойти с ума, влюбиться так неосторожно
|
||||
Найти тебя, не отпускать ни днём, ни ночью
|
||||
Всё невозможное - возможно, знаю точно!
|
||||
На-на-на-на (на-на-на-на), а-а, а-а
|
||||
На-на-на-на (на-на-на-на), а-а, а-а""");
|
||||
|
||||
render.setButton(ButtonNumber.BUTTON_1, "Hello world!\nHello world!\nHello world!\nHello world!\nHello world!", ctx -> {});
|
||||
render.setButton(ButtonNumber.BUTTON_2, "I am a teapot", ctx -> {});
|
||||
render.setButton(ButtonNumber.BUTTON_3, "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nZaebumba", ctx -> {});
|
||||
render.setButton(ButtonNumber.BUTTON_4, "msb3 is top!", ctx -> {});
|
||||
|
||||
render.show();
|
||||
PictureBanner.TEST.show(player);
|
||||
// var render = new DialogueRenderer(player, DialogueTheme.builder().build());
|
||||
// render.setText("""
|
||||
// Абсолютно точно.
|
||||
// Я знаю точнo - невозможное возможно
|
||||
// Сойти с ума, влюбиться так неосторoжно
|
||||
// Найти тебя, не отпускать ни днём, ни ночью
|
||||
// Всё невозможное - возможно, знаю точно!
|
||||
// А где тебя искать, прошу ты мне ответь
|
||||
// В какие города мне за тобой лететь
|
||||
// Я готов на край Земли, я всё должен объяснить
|
||||
// Пойми, что без тебя я не умею жить
|
||||
// Я знаю точно - невозможное возможно
|
||||
// Сойти с ума, влюбиться так неосторожно
|
||||
// Найти тебя, не отпускать ни днём, ни ночью
|
||||
// Всё невозможное - возможно, знаю точно!
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а
|
||||
// Всё готов делить, с тобой я пополам
|
||||
// Ты только мне поверь, я сделал выбор сам
|
||||
// Дай же мне последний шанс, я всё должен объяснить
|
||||
// Пойми, что без тебя я не умею жить
|
||||
// Я знаю точно - невозможное возможно
|
||||
// Сойти с ума, влюбиться так неосторожно
|
||||
// Найти тебя, не отпускать ни днём, ни ночью
|
||||
// Всё невозможное - возможно, знаю точно!
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а
|
||||
// Я знаю точно - невозможное возможно
|
||||
// Сойти с ума, влюбиться так неосторожно
|
||||
// Найти тебя, не отпускать ни днём, ни ночью
|
||||
// Всё невозможное - возможно, знаю точно!
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а
|
||||
// На-на-на-на (на-на-на-на), а-а, а-а""");
|
||||
//
|
||||
// render.setButton(ButtonNumber.BUTTON_1, "Всем привет!", ctx -> {});
|
||||
// render.setButton(ButtonNumber.BUTTON_2, "I am a teapot", ctx -> {});
|
||||
// render.setButton(ButtonNumber.BUTTON_3, "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nZaebumba", ctx -> {});
|
||||
// render.setButton(ButtonNumber.BUTTON_4, "msb3 is top!", ctx -> {});
|
||||
//
|
||||
// render.show();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeResources(ResourcePack resourcePack) {
|
||||
|
||||
ClassPreLoader.preload(PictureBanner.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -5,6 +5,7 @@ import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import ru.dragonestia.msb3.api.entity.PickableItem;
|
||||
import ru.dragonestia.msb3.api.item.ItemUtil;
|
||||
import ru.dragonestia.msb3.api.resource.DialogueResources;
|
||||
import ru.dragonestia.msb3.api.resource.MonologueResources;
|
||||
@ -77,6 +78,7 @@ public final class ServerBootstrap {
|
||||
|
||||
private void initDefaultModules() {
|
||||
ItemUtil.init();
|
||||
PickableItem.registerEvent();
|
||||
}
|
||||
|
||||
private void compileResourcePack() {
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package ru.dragonestia.msb3.api.entity;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.ItemEntity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.item.PickupItemEvent;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
public class PickableItem extends ItemEntity {
|
||||
|
||||
public PickableItem(@NotNull ItemStack itemStack) {
|
||||
super(itemStack);
|
||||
|
||||
setPickable(true);
|
||||
setMergeable(false);
|
||||
setPickupDelay(500, ChronoUnit.MILLIS);
|
||||
}
|
||||
|
||||
public static void registerEvent() {
|
||||
MinecraftServer.getGlobalEventHandler().addListener(PickupItemEvent.class, event -> {
|
||||
if (!(event.getItemEntity() instanceof PickableItem)) return;
|
||||
if (!(event.getEntity() instanceof Player player)) return;
|
||||
|
||||
var itemEntity = event.getItemEntity();
|
||||
var inv = player.getInventory();
|
||||
var item = itemEntity.getItemStack();
|
||||
|
||||
if (!inv.addItemStack(item)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,8 @@ public class ResourcePackRepositoryModule {
|
||||
server.start();
|
||||
|
||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
|
||||
if (!event.isFirstSpawn()) return;
|
||||
|
||||
var player = event.getPlayer();
|
||||
|
||||
player.sendResourcePacks(ResourcePackRequest.resourcePackRequest()
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
package ru.dragonestia.msb3.api.ui;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
|
||||
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||
import ru.dragonestia.msb3.resource.Resources;
|
||||
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
||||
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||
import ru.dragonestia.msb3.resource.utils.ImageUtils;
|
||||
import team.unnamed.creative.base.Writable;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PictureBanner {
|
||||
|
||||
public static final int CHEST_GUI_WIDTH = 176;
|
||||
|
||||
public static PictureBanner TEST = new PictureBanner("test", ResourceFromJar.of("glyphs/test_banner.png"));
|
||||
|
||||
private final GlyphImage glyph1;
|
||||
private final GlyphImage glyph2;
|
||||
private final GlyphImage glyph3;
|
||||
private final GlyphImage glyph4;
|
||||
|
||||
public PictureBanner(String identifier, Writable writable) {
|
||||
BufferedImage image;
|
||||
Writable part1;
|
||||
Writable part2;
|
||||
Writable part3;
|
||||
Writable part4;
|
||||
|
||||
try (var steam = new ByteArrayInputStream(writable.toByteArray())) {
|
||||
image = ImageIO.read(steam);
|
||||
var w = image.getWidth();
|
||||
var h = image.getHeight();
|
||||
|
||||
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2));
|
||||
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2));
|
||||
part3 = ImageUtils.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2));
|
||||
part4 = ImageUtils.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
glyph1 = Resources.createGlyph(
|
||||
Key.key("msb3", "banner/" + identifier + "/1"),
|
||||
part1,
|
||||
GlyphPositions.guiBackground.height() / 2,
|
||||
GlyphPositions.guiBackground.topPartsY()
|
||||
);
|
||||
glyph2 = Resources.createGlyph(
|
||||
Key.key("msb3", "banner/" + identifier + "/2"),
|
||||
part2,
|
||||
GlyphPositions.guiBackground.height() / 2,
|
||||
GlyphPositions.guiBackground.topPartsY()
|
||||
);
|
||||
glyph3 = Resources.createGlyph(
|
||||
Key.key("msb3", "banner/" + identifier + "/3"),
|
||||
part3,
|
||||
GlyphPositions.guiBackground.height() / 2,
|
||||
GlyphPositions.guiBackground.bottomPartsY()
|
||||
);
|
||||
glyph4 = Resources.createGlyph(
|
||||
Key.key("msb3", "banner/" + identifier + "/4"),
|
||||
part4,
|
||||
GlyphPositions.guiBackground.height() / 2,
|
||||
GlyphPositions.guiBackground.bottomPartsY()
|
||||
);
|
||||
}
|
||||
|
||||
public void show(Player player) {
|
||||
var builder = new GlyphComponentBuilder();
|
||||
builder.append(CHEST_GUI_WIDTH / 2 + 2 - glyph1.width(), glyph1);
|
||||
builder.append(CHEST_GUI_WIDTH / 2 + 1, glyph2);
|
||||
builder.append(CHEST_GUI_WIDTH / 2 + 2 - glyph3.width(), glyph3);
|
||||
builder.append(CHEST_GUI_WIDTH / 2 + 1, glyph4);
|
||||
|
||||
var inv = new Inventory(InventoryType.CHEST_6_ROW, builder.build());
|
||||
player.openInventory(inv);
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,22 @@ package ru.dragonestia.msb3.api.world.chunk;
|
||||
|
||||
import net.minestom.server.instance.DynamicChunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.LightingChunk;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OutOfBoundsChunk extends DynamicChunk {
|
||||
public interface OutOfBoundsChunk {
|
||||
|
||||
public OutOfBoundsChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||
super(instance, chunkX, chunkZ);
|
||||
class Dynamic extends DynamicChunk implements OutOfBoundsChunk {
|
||||
|
||||
public Dynamic(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||
super(instance, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
|
||||
class Lighting extends LightingChunk implements OutOfBoundsChunk {
|
||||
|
||||
public Lighting(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||
super(instance, chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ public class PreloadedAnvilChunkLoader implements IChunkLoader {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
var sourceChunkData = source.provideChunk(chunkX, chunkZ);
|
||||
if (sourceChunkData.isEmpty()) {
|
||||
return new OutOfBoundsChunk(instance, chunkX, chunkZ);
|
||||
return new OutOfBoundsChunk.Dynamic(instance, chunkX, chunkZ);
|
||||
}
|
||||
return new SharedChunk(instance, chunkX, chunkZ, sourceChunkData.get());
|
||||
});
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package ru.dragonestia.editor.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.dragonestia.editor.controller.mapper.DialogueMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class DialogueController {
|
||||
|
||||
@GetMapping("/api/dialogues")
|
||||
List<DialogueMapper> allDialogues() {
|
||||
return List.of(); // TODO
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package ru.dragonestia.editor.controller.mapper;
|
||||
|
||||
import ru.dragonestia.editor.model.DialogueContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public record DialogueMapper(
|
||||
String groupId,
|
||||
String id,
|
||||
String text,
|
||||
List<Answer> answers
|
||||
) {
|
||||
|
||||
public static DialogueMapper fromEntity(DialogueContext context) {
|
||||
var buttons = new ArrayList<Answer>();
|
||||
for (var button: context.getAnswers()) {
|
||||
var actionData = new HashMap<String, String>();
|
||||
for (var actionParam: button.getAction().getFields()) {
|
||||
actionData.put(actionParam.getIdentifier(), actionParam.getValue());
|
||||
}
|
||||
|
||||
var conditions = new ArrayList<Condition>();
|
||||
for (var condition: button.getConditions()) {
|
||||
var conditionData = new HashMap<String, String>();
|
||||
for (var param: condition.getFields()) {
|
||||
conditionData.put(param.getIdentifier(), param.getValue());
|
||||
}
|
||||
conditions.add(new Condition(condition.getIdentifier(), conditionData));
|
||||
}
|
||||
|
||||
buttons.add(new Answer(button.getIdentifier(), button.getText(), button.getAction().getIdentifier(), actionData, conditions));
|
||||
}
|
||||
return new DialogueMapper(context.getGroupId(), context.getGroupId(), context.getText(), buttons);
|
||||
}
|
||||
|
||||
public record Answer(
|
||||
String id,
|
||||
String text,
|
||||
String actionId,
|
||||
Map<String, String> actionData,
|
||||
List<Condition> conditions
|
||||
) {}
|
||||
|
||||
public record Condition(
|
||||
String id,
|
||||
Map<String, String> data
|
||||
) {}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package ru.dragonestia.editor.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DialogueAction {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private String description;
|
||||
private List<Field> fields = new ArrayList<>();
|
||||
private boolean builtIn;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Field {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private String description;
|
||||
private FieldType type;
|
||||
private String defaultValue;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package ru.dragonestia.editor.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DialogueCondition {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private String description;
|
||||
private ArrayList<Field> fields = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Field {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private String description;
|
||||
private FieldType type;
|
||||
private String defaultValue;
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,54 @@ package ru.dragonestia.editor.model;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DialogueContext {
|
||||
|
||||
private String groupId;
|
||||
private String id;
|
||||
private String text;
|
||||
private String comment;
|
||||
private ArrayList<Answer> answers = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Answer {
|
||||
|
||||
private String identifier;
|
||||
private String text;
|
||||
private String comment;
|
||||
private Action action;
|
||||
private ArrayList<Condition> conditions = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Action {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private ArrayList<Field> fields = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Condition {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private ArrayList<Field> fields = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Field {
|
||||
|
||||
private String identifier;
|
||||
private String name;
|
||||
private FieldType type;
|
||||
private String value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package ru.dragonestia.editor.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum FieldType {
|
||||
STRING("Строка", input -> true),
|
||||
TEXT("Текст", input -> true),
|
||||
INTEGER("Целое число", input -> {
|
||||
try {
|
||||
Integer.parseInt(input);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
BOOLEAN("Булево значение", input -> switch (input.toLowerCase()) {
|
||||
case "true", "false" -> true;
|
||||
default -> false;
|
||||
}),
|
||||
DOUBLE("Число с плавающей точкой", input -> {
|
||||
try {
|
||||
Double.parseDouble(input);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
private final String name;
|
||||
private final Predicate<String> validator;
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package ru.dragonestia.editor.page;
|
||||
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import ru.dragonestia.editor.page.browser.BrowserPage;
|
||||
import ru.dragonestia.editor.page.browser.TabContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DialogueGroupPage extends TabContainer {
|
||||
|
||||
private final String groupIdentifier;
|
||||
private final TextField fieldSearch;
|
||||
private final Grid<DialogueEntry> grid;
|
||||
|
||||
public DialogueGroupPage(BrowserPage browser, String groupIdentifier) {
|
||||
super(browser, "Группа: " + groupIdentifier);
|
||||
this.groupIdentifier = groupIdentifier;
|
||||
|
||||
add(new H2(groupIdentifier));
|
||||
add(new Paragraph(
|
||||
"Здесь содержатся диалоги, которые связаны с данной группой. " +
|
||||
"Нажмите на диалог в таблице чтобы редактировать или удалить его. " +
|
||||
"Если хотите создать новый диалог, то нажмите кнопку для создания ниже. "
|
||||
));
|
||||
|
||||
var buttonsLayout = new HorizontalLayout();
|
||||
buttonsLayout.setWidth("100%");
|
||||
add(buttonsLayout);
|
||||
buttonsLayout.add(createButtonNewDialogue());
|
||||
buttonsLayout.add(createButtonUpdateDialogues());
|
||||
|
||||
add(fieldSearch = createFieldSearch());
|
||||
add(grid = createGrid());
|
||||
// TODO: init grid data
|
||||
grid.setItems(List.of(
|
||||
new DialogueEntry("test1", ""),
|
||||
new DialogueEntry("test2", "а ывп выпыв пку пукфпкаырп ыварукып куруке авыфп авып"),
|
||||
new DialogueEntry("fsdfdsfds", "111"),
|
||||
new DialogueEntry("hello", "Приветственный диалог с игроком")
|
||||
));
|
||||
}
|
||||
|
||||
private Button createButtonUpdateDialogues() {
|
||||
var button = new Button("Обновить список диалогов", VaadinIcon.REFRESH.create(), event -> {
|
||||
// TODO
|
||||
});
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createButtonNewDialogue() {
|
||||
var button = new Button("Создать новый диалог", VaadinIcon.PLUS.create(), e -> {
|
||||
// TODO
|
||||
});
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
return button;
|
||||
}
|
||||
|
||||
private TextField createFieldSearch() {
|
||||
var field = new TextField();
|
||||
field.setPlaceholder("Поиск группы по идентификатору");
|
||||
field.setPrefixComponent(VaadinIcon.SEARCH.create());
|
||||
field.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
field.addValueChangeListener(e -> {
|
||||
var input = e.getValue().trim().toLowerCase();
|
||||
|
||||
// TODO
|
||||
});
|
||||
return field;
|
||||
}
|
||||
|
||||
private Grid<DialogueEntry> createGrid() {
|
||||
var grid = new Grid<DialogueEntry>();
|
||||
grid.addColumn(DialogueEntry::identifier).setHeader("Идентификатор").setWidth("15%").setFlexGrow(0);
|
||||
grid.addColumn(DialogueEntry::comment).setHeader("Комментарий");
|
||||
grid.addItemClickListener(e -> {
|
||||
var dialogueIdentifier = e.getItem().identifier();
|
||||
// TODO
|
||||
|
||||
browser.openTab(new DialoguePage(browser, groupIdentifier, "test_id"));
|
||||
});
|
||||
return grid;
|
||||
}
|
||||
|
||||
public record DialogueEntry(String identifier, String comment) {}
|
||||
}
|
||||
@ -0,0 +1,325 @@
|
||||
package ru.dragonestia.editor.page;
|
||||
|
||||
import com.vaadin.flow.component.Text;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.html.ListItem;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.Scroller;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import ru.dragonestia.editor.model.DialogueAction;
|
||||
import ru.dragonestia.editor.model.DialogueContext;
|
||||
import ru.dragonestia.editor.model.FieldType;
|
||||
import ru.dragonestia.editor.page.browser.BrowserPage;
|
||||
import ru.dragonestia.editor.page.browser.TabContainer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DialoguePage extends TabContainer {
|
||||
|
||||
private final static Random random = new Random();
|
||||
|
||||
private final String groupIdentifier;
|
||||
private final String dialogueIdentifier;
|
||||
private final TextArea fieldText;
|
||||
private final TextArea fieldComment;
|
||||
private final Button buttonNewAnswer;
|
||||
private final VerticalLayout layoutAnswers;
|
||||
private final List<Answer> answers = new ArrayList<>();
|
||||
|
||||
private DialogueContext dialogueContext = new DialogueContext();
|
||||
|
||||
public DialoguePage(BrowserPage browser, String groupIdentifier, String dialogueIdentifier) {
|
||||
super(browser, "Диалог: %s/%s".formatted(groupIdentifier, dialogueIdentifier));
|
||||
this.groupIdentifier = groupIdentifier;
|
||||
this.dialogueIdentifier = dialogueIdentifier;
|
||||
|
||||
var layout = new HorizontalLayout();
|
||||
layout.setPadding(false);
|
||||
layout.setWidth(100, Unit.PERCENTAGE);
|
||||
add(layout);
|
||||
var leftLayout = new VerticalLayout();
|
||||
leftLayout.setWidth(45, Unit.PERCENTAGE);
|
||||
leftLayout.setPadding(false);
|
||||
layout.add(leftLayout);
|
||||
var rightLayout = new VerticalLayout();
|
||||
rightLayout.setPadding(false);
|
||||
layout.add(rightLayout);
|
||||
|
||||
leftLayout.add(createFieldIdentifier());
|
||||
leftLayout.add(fieldText = createFieldText());
|
||||
leftLayout.add(createTextHelping());
|
||||
|
||||
var controlLayout = new HorizontalLayout();
|
||||
controlLayout.setWidth(100, Unit.PERCENTAGE);
|
||||
controlLayout.setPadding(false);
|
||||
leftLayout.add(controlLayout);
|
||||
controlLayout.add(createButtonSave());
|
||||
controlLayout.add(createButtonDelete());
|
||||
|
||||
leftLayout.add(fieldComment = createFieldComment());
|
||||
|
||||
rightLayout.add(new H3("Ответы диалогов"));
|
||||
rightLayout.add(buttonNewAnswer = createButtonNewAnswer());
|
||||
|
||||
layoutAnswers = new VerticalLayout();
|
||||
layoutAnswers.setPadding(false);
|
||||
rightLayout.add(layoutAnswers);
|
||||
updateAnswers();
|
||||
|
||||
//add(new DialogEditor(new DialogueContext()));
|
||||
}
|
||||
|
||||
private TextField createFieldIdentifier() {
|
||||
var field = new TextField("Идентификатор группы/идентификатор диалога");
|
||||
field.setHelperText("Идентификатор диалога изменять нельзя");
|
||||
field.setValue("%s/%s".formatted(groupIdentifier, dialogueIdentifier));
|
||||
field.setReadOnly(true);
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
return field;
|
||||
}
|
||||
|
||||
private TextArea createFieldText() {
|
||||
var field = new TextArea("Текст диалога");
|
||||
field.setHeight(20, Unit.REM);
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
return field;
|
||||
}
|
||||
|
||||
private VerticalLayout createTextHelping() {
|
||||
var layout = new VerticalLayout();
|
||||
layout.setPadding(false);
|
||||
layout.add(new Text("Здесь описаны подсказки с плейсхолдерами для диалога:")); // TODO
|
||||
layout.add(new ListItem("Плейсхолдер 1"));
|
||||
layout.add(new ListItem("Плейсхолдер 2"));
|
||||
layout.add(new ListItem("Плейсхолдер 3"));
|
||||
return layout;
|
||||
}
|
||||
|
||||
private TextArea createFieldComment() {
|
||||
var field = new TextArea("Комментарий");
|
||||
field.setHelperText("Комментарием может являться любая пометка с кратким описанием диалога. Этот комментарий видно в списке диалогов на сайте.");
|
||||
field.setMinHeight(10, Unit.REM);
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
return field;
|
||||
}
|
||||
|
||||
private Button createButtonNewAnswer() {
|
||||
var button = new Button("Добавить ответ диалога", VaadinIcon.PLUS.create());
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
button.addClickListener(e -> {
|
||||
var answer = new Answer();
|
||||
answer.deletion = () -> deleteAnswer(answer);
|
||||
answers.add(answer);
|
||||
updateAnswers();
|
||||
});
|
||||
button.setWidth(100, Unit.PERCENTAGE);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createButtonSave() {
|
||||
var button = new Button("Сохранить", VaadinIcon.DATABASE.create(), e -> {
|
||||
// TODO
|
||||
});
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
button.setWidth(49, Unit.PERCENTAGE);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createButtonDelete() {
|
||||
var button = new Button("Удалить", VaadinIcon.TRASH.create(), e -> sendDeletionConfirm());
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
button.setWidth(49, Unit.PERCENTAGE);
|
||||
return button;
|
||||
}
|
||||
|
||||
private void sendDeletionConfirm() {
|
||||
var dialog = new Dialog("Удаление диалога");
|
||||
dialog.setWidth(30, Unit.REM);
|
||||
dialog.getFooter().add(new Button("Отмена", e -> dialog.close()));
|
||||
|
||||
var randomNumber = Integer.toString(random.nextInt(100, 999));
|
||||
|
||||
var layout = new VerticalLayout();
|
||||
dialog.add(layout);
|
||||
layout.add(new Paragraph("Подтвердите что вы хотите удалить диалог. Введите код ниже в поле для ввода чтобы подтвердить удаление."));
|
||||
|
||||
var codeLayout = new H3(randomNumber);
|
||||
layout.add(codeLayout);
|
||||
|
||||
var buttonConfirm = new Button("Подтвердить удаление", e -> {
|
||||
// TODO
|
||||
dialog.close();
|
||||
});
|
||||
buttonConfirm.setEnabled(false);
|
||||
buttonConfirm.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
buttonConfirm.setWidth(100, Unit.PERCENTAGE);
|
||||
|
||||
var fieldConfirmation = new TextField();
|
||||
fieldConfirmation.setPlaceholder("Введите код, написанный выше");
|
||||
fieldConfirmation.setWidth(100, Unit.PERCENTAGE);
|
||||
fieldConfirmation.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
fieldConfirmation.addValueChangeListener(e -> {
|
||||
buttonConfirm.setEnabled(randomNumber.equals(e.getValue()));
|
||||
});
|
||||
layout.add(fieldConfirmation);
|
||||
|
||||
layout.add(buttonConfirm);
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
private void updateAnswers() {
|
||||
buttonNewAnswer.setEnabled(answers.size() < 4);
|
||||
|
||||
layoutAnswers.removeAll();
|
||||
for (var answer: answers) {
|
||||
layoutAnswers.add(answer);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteAnswer(Answer answer) {
|
||||
answers.removeIf(target -> answer.uuid.equals(target.uuid));
|
||||
updateAnswers();
|
||||
}
|
||||
|
||||
public static class Answer extends HorizontalLayout {
|
||||
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
private Runnable deletion;
|
||||
private final VerticalLayout layoutAction;
|
||||
|
||||
public Answer() {
|
||||
var layoutLeft = new VerticalLayout();
|
||||
add(layoutLeft);
|
||||
var layoutRight = new VerticalLayout();
|
||||
add(layoutRight);
|
||||
layoutAction = new VerticalLayout();
|
||||
layoutAction.setPadding(false);
|
||||
|
||||
layoutLeft.add(createFieldText());
|
||||
layoutLeft.add(createSelectAction());
|
||||
layoutLeft.add(layoutAction);
|
||||
|
||||
layoutRight.add(createButtonDeleteAnswer());
|
||||
layoutRight.add(createFieldIdentifier());
|
||||
layoutRight.add(createFieldComment());
|
||||
|
||||
setWidth(100, Unit.PERCENTAGE);
|
||||
getStyle().set("background-color", "#EEEEEE");
|
||||
getStyle().set("border-radius", "26px");
|
||||
}
|
||||
|
||||
private TextField createFieldText() {
|
||||
var field = new TextField("Текст кнопки");
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
return field;
|
||||
}
|
||||
|
||||
private Button createButtonDeleteAnswer() {
|
||||
var button = new Button("Удалить ответ", VaadinIcon.TRASH.create(), e -> {
|
||||
if (deletion == null) return;
|
||||
deletion.run();
|
||||
});
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
|
||||
button.setWidth(100, Unit.PERCENTAGE);
|
||||
return button;
|
||||
}
|
||||
|
||||
private TextField createFieldIdentifier() {
|
||||
var field = new TextField("Идентификатор кнопки");
|
||||
field.setHelperText("Может быть пустым. Этот параметр нужен чтобы запоминать какие кнопки нажимал игрок чтобы потом делать различные проверки на это.");
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
return field;
|
||||
}
|
||||
|
||||
private TextArea createFieldComment() {
|
||||
var field = new TextArea("Комментарий");
|
||||
field.setHelperText("Комментарием может являться любая пометка с кратким описанием ответа диалога.");
|
||||
field.setWidth(100, Unit.PERCENTAGE);
|
||||
field.setHeight(10, Unit.REM);
|
||||
return field;
|
||||
}
|
||||
|
||||
private ComboBox<DialogueAction> createSelectAction() {
|
||||
var comboBox = new ComboBox<DialogueAction>("Действие ответа диалога");
|
||||
comboBox.setHelperText("Выполняется, когда игрок нажимает на кнопку выбора ответа внутри диалога");
|
||||
comboBox.setWidth(100, Unit.PERCENTAGE);
|
||||
comboBox.setItemLabelGenerator(action -> "%s [%s]".formatted(action.getName(), action.getIdentifier()));
|
||||
|
||||
var items = List.of(
|
||||
new DialogueAction("close", "Закрыть диалог", "Просто закрывает диалог", new ArrayList<>(), true),
|
||||
new DialogueAction("dialogue", "Перейти к диалогу", "Перейти к другому диалогу", List.of(
|
||||
new DialogueAction.Field("groupId", "Id-группы диалога", "", FieldType.STRING, ""),
|
||||
new DialogueAction.Field("dialogueId", "Id диалога", "", FieldType.STRING, "")
|
||||
), true),
|
||||
new DialogueAction("test_params", "Тест всех полей", "", List.of(
|
||||
new DialogueAction.Field("param1", "Строка", "", FieldType.STRING, ""),
|
||||
new DialogueAction.Field("param2", "Текст", "", FieldType.TEXT, ""),
|
||||
new DialogueAction.Field("param3", "Булево значение", "", FieldType.BOOLEAN, ""),
|
||||
new DialogueAction.Field("param4", "Целое число", "", FieldType.INTEGER, ""),
|
||||
new DialogueAction.Field("param5", "Число с плавающей точкой", "", FieldType.DOUBLE, "")
|
||||
), true)
|
||||
);
|
||||
comboBox.setItems(items);
|
||||
comboBox.setValue(items.getFirst());
|
||||
comboBox.addValueChangeListener(e -> {
|
||||
updateAction(e.getValue(), new HashMap<>());
|
||||
});
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
private void updateAction(DialogueAction action, Map<String, String> params) {
|
||||
layoutAction.removeAll();
|
||||
var fieldMapper = new HashMap<String, Supplier<String>>();
|
||||
for (var param: action.getFields()) {
|
||||
var component = switch (param.getType()) {
|
||||
case STRING -> {
|
||||
var field = new TextField(param.getName());
|
||||
field.setValue(param.getDefaultValue());
|
||||
fieldMapper.put(param.getIdentifier(), field::getValue);
|
||||
yield field;
|
||||
}
|
||||
case TEXT -> {
|
||||
var field = new TextArea(param.getName());
|
||||
field.setValue(param.getDefaultValue());
|
||||
fieldMapper.put(param.getIdentifier(), field::getValue);
|
||||
yield field;
|
||||
}
|
||||
case BOOLEAN -> {
|
||||
var field = new Checkbox(param.getName());
|
||||
field.setValue(Boolean.parseBoolean(param.getDefaultValue()));
|
||||
fieldMapper.put(param.getIdentifier(), () -> Boolean.toString(field.getValue()));
|
||||
yield field;
|
||||
}
|
||||
case INTEGER -> {
|
||||
var field = new IntegerField(param.getName());
|
||||
fieldMapper.put(param.getIdentifier(), () -> Integer.toString(field.getValue()));
|
||||
yield field;
|
||||
}
|
||||
case DOUBLE -> {
|
||||
var field = new NumberField(param.getName());
|
||||
fieldMapper.put(param.getIdentifier(), () -> Double.toString(field.getValue()));
|
||||
yield field;
|
||||
}
|
||||
};
|
||||
|
||||
component.setWidth(100, Unit.PERCENTAGE);
|
||||
layoutAction.add(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
166
editor/src/main/java/ru/dragonestia/editor/page/HomePage.java
Normal file
166
editor/src/main/java/ru/dragonestia/editor/page/HomePage.java
Normal file
@ -0,0 +1,166 @@
|
||||
package ru.dragonestia.editor.page;
|
||||
|
||||
import com.vaadin.flow.component.Html;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.*;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import ru.dragonestia.editor.page.browser.BrowserPage;
|
||||
import ru.dragonestia.editor.page.browser.TabContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class HomePage extends TabContainer {
|
||||
|
||||
public HomePage(BrowserPage browser) {
|
||||
super(browser, "Домашняя страница");
|
||||
|
||||
add(new H3("Диалоги"));
|
||||
addLink("Управление группами диалогов", this::findOrCreateDialog);
|
||||
addLink("Список всех групп диалогов", this::listAllDialogueGroups);
|
||||
add(new H3("Действия ответов диалога"));
|
||||
addLink("Создание/редактирование действий ответов диалога", this::findOrCreateDialogAction);
|
||||
addLink("Список всех действий ответов диалога", this::listAllDialogueActions);
|
||||
add(new H3("Условия ответов диалога"));
|
||||
addLink("Создание/редактирование условий ответов диалога", this::findOrCreateDialogCondition);
|
||||
addLink("Список всех ответов диалога", this::listAllDialogueConditions);
|
||||
}
|
||||
|
||||
private void addLink(String label, BiConsumer<VerticalLayout, Runnable> content) {
|
||||
var component = new Span(new Html("<a href='#'>" + label + "</a>"));
|
||||
component.addClickListener(event -> {
|
||||
var dialog = new Dialog();
|
||||
dialog.setMinHeight(30, Unit.PERCENTAGE);
|
||||
dialog.setWidth(40, Unit.PERCENTAGE);
|
||||
|
||||
dialog.getHeader().add(new H2(label));
|
||||
|
||||
var layout = new VerticalLayout();
|
||||
layout.setPadding(false);
|
||||
content.accept(layout, dialog::close);
|
||||
dialog.add(layout);
|
||||
|
||||
var closeButton = new Button("Close");
|
||||
closeButton.addThemeVariants();
|
||||
closeButton.addClickListener(e -> dialog.close());
|
||||
dialog.getFooter().add(closeButton);
|
||||
|
||||
dialog.open();
|
||||
});
|
||||
add(new ListItem(component));
|
||||
}
|
||||
|
||||
private void findOrCreateDialog(VerticalLayout layout, Runnable closeWindow) {
|
||||
layout.add(new Paragraph(
|
||||
"Введите идентификатор группы диалога чтобы перейти к созданию или редактированию группы диалога. " +
|
||||
"Группы диалогов нужны чтобы упростить группировку диалогов. "
|
||||
));
|
||||
|
||||
var fieldIdentifier = new TextField("Идентификатор группы");
|
||||
fieldIdentifier.setHelperText("Идентификатор может содержать только символы английского алфавита, цифры и символ нижнего подчеркивания");
|
||||
fieldIdentifier.setWidth(100, Unit.PERCENTAGE);
|
||||
layout.add(fieldIdentifier);
|
||||
|
||||
var buttons = new HorizontalLayout();
|
||||
buttons.setWidth(100, Unit.PERCENTAGE);
|
||||
buttons.setJustifyContentMode(JustifyContentMode.CENTER);
|
||||
layout.add(buttons);
|
||||
|
||||
var buttonCheckGroup = new Button("Проверить существование группы", e -> {
|
||||
// TODO
|
||||
});
|
||||
buttonCheckGroup.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
buttonCheckGroup.setWidth(49, Unit.PERCENTAGE);
|
||||
buttons.add(buttonCheckGroup);
|
||||
|
||||
var createOrFind = new Button("Создать/редактировать", e -> {
|
||||
var groupIdentifier = fieldIdentifier.getValue().trim().toLowerCase();
|
||||
|
||||
if (groupIdentifier.isEmpty()) return;
|
||||
if (!groupIdentifier.matches("^[aA-zZ\\d_]+$")) {
|
||||
Notification.show("Идентификатор группы содержит недопустимые символы.")
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
browser.openTab(new DialogueGroupPage(browser, groupIdentifier));
|
||||
closeWindow.run();
|
||||
});
|
||||
createOrFind.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
|
||||
createOrFind.setWidth(49, Unit.PERCENTAGE);
|
||||
buttons.add(createOrFind);
|
||||
}
|
||||
|
||||
private void listAllDialogueGroups(VerticalLayout layout, Runnable closeWindow) {
|
||||
var buttonRefresh = new Button("Обновить список", VaadinIcon.REFRESH.create());
|
||||
layout.add(buttonRefresh);
|
||||
|
||||
var fieldSearch = new TextField();
|
||||
fieldSearch.setPlaceholder("Поиск группы по идентификатору");
|
||||
fieldSearch.setPrefixComponent(VaadinIcon.SEARCH.create());
|
||||
fieldSearch.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
fieldSearch.setWidth(100, Unit.PERCENTAGE);
|
||||
layout.add(fieldSearch);
|
||||
|
||||
var grid = new Grid<DialogGroupEntry>();
|
||||
var data = new ArrayList<DialogGroupEntry>();
|
||||
// TODO: initial data loading
|
||||
grid.setItems(data);
|
||||
grid.addColumn(DialogGroupEntry::id).setHeader("Id группы").setWidth("25%").setFlexGrow(0);
|
||||
grid.addColumn(DialogGroupEntry::dialoguesInside).setHeader("Кол-во диалогов").setWidth("20%").setFlexGrow(0);
|
||||
grid.addComponentColumn(obj -> new Paragraph(obj.comment())).setHeader("Комментарий");
|
||||
grid.addItemClickListener(e -> {
|
||||
var item = e.getItem();
|
||||
// TODO: open dialogue group
|
||||
|
||||
closeWindow.run();
|
||||
});
|
||||
layout.add(grid);
|
||||
|
||||
fieldSearch.addValueChangeListener(e -> {
|
||||
var input = e.getValue().trim().toLowerCase();
|
||||
var newList = new ArrayList<>(data);
|
||||
newList.removeIf(entry -> !entry.id().toLowerCase().startsWith(input));
|
||||
grid.setItems(newList);
|
||||
});
|
||||
|
||||
buttonRefresh.addClickListener(e -> {
|
||||
// TODO
|
||||
});
|
||||
|
||||
layout.add(new Paragraph(
|
||||
"В этом списке отображаются группы, которые имеют хоть какое-то количество диалогов внутри. " +
|
||||
"Нажмите на строчку с диалогом чтобы заглянуть внутрь. "
|
||||
));
|
||||
}
|
||||
|
||||
private void findOrCreateDialogAction(VerticalLayout layout, Runnable closeWindow) {
|
||||
|
||||
}
|
||||
|
||||
private void listAllDialogueActions(VerticalLayout layout, Runnable closeWindow) {
|
||||
|
||||
}
|
||||
|
||||
private void findOrCreateDialogCondition(VerticalLayout layout, Runnable closeWindow) {
|
||||
|
||||
}
|
||||
|
||||
private void listAllDialogueConditions(VerticalLayout layout, Runnable closeWindow) {
|
||||
|
||||
}
|
||||
|
||||
private record DialogGroupEntry(String id, int dialoguesInside, String comment) {}
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
package ru.dragonestia.editor.page;
|
||||
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||
import com.vaadin.flow.router.BeforeLeaveEvent;
|
||||
import com.vaadin.flow.router.BeforeLeaveObserver;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class Page extends VerticalLayout implements BeforeEnterObserver, BeforeLeaveObserver {
|
||||
|
||||
protected void init(QueryParams params) {}
|
||||
|
||||
protected void destroy() {}
|
||||
|
||||
@Override
|
||||
public final void beforeEnter(BeforeEnterEvent event) {
|
||||
init(key -> event.getRouteParameters().get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void beforeLeave(BeforeLeaveEvent event) {
|
||||
destroy();
|
||||
}
|
||||
|
||||
public interface QueryParams {
|
||||
|
||||
Optional<String> String(String key);
|
||||
|
||||
default Optional<Integer> Integer(String key) {
|
||||
return String(key).map(str -> {
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default Optional<Long> Long(String key) {
|
||||
return String(key).map(str -> {
|
||||
try {
|
||||
return Long.parseLong(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default Optional<Float> Float(String key) {
|
||||
return String(key).map(str -> {
|
||||
try {
|
||||
return Float.parseFloat(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default Optional<Double> Double(String key) {
|
||||
return String(key).map(str -> {
|
||||
try {
|
||||
return Double.parseDouble(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default Optional<Boolean> Boolean(String key) {
|
||||
return String(key).map(str -> {
|
||||
try {
|
||||
return Boolean.parseBoolean(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package ru.dragonestia.editor.page;
|
||||
|
||||
import com.vaadin.flow.router.Route;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import ru.dragonestia.editor.component.DialogEditor;
|
||||
import ru.dragonestia.editor.model.DialogueContext;
|
||||
|
||||
@Route("/")
|
||||
public class TestPage extends Page {
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
var ctx = new DialogueContext();
|
||||
add(new DialogEditor(ctx));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package ru.dragonestia.editor.page.browser;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.tabs.Tab;
|
||||
import com.vaadin.flow.component.tabs.TabSheet;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import ru.dragonestia.editor.page.HomePage;
|
||||
|
||||
@Route("/")
|
||||
public class BrowserPage extends VerticalLayout {
|
||||
|
||||
private final TabSheet tabs;
|
||||
|
||||
public BrowserPage() {
|
||||
tabs = new TabSheet();
|
||||
tabs.setWidth(100, Unit.PERCENTAGE);
|
||||
//tabs.setSuffixComponent(createNewTabButton());
|
||||
add(tabs);
|
||||
|
||||
openHomePage();
|
||||
}
|
||||
|
||||
private Component createNewTabButton() {
|
||||
return new Button(VaadinIcon.PLUS.create(), event -> openHomePage());
|
||||
}
|
||||
|
||||
private void openHomePage() {
|
||||
var tab = new Tab(new Span(VaadinIcon.HOME.create()));
|
||||
var style = tab.getStyle();
|
||||
style.setPaddingTop("0rem");
|
||||
style.setPaddingBottom("0rem");
|
||||
|
||||
tabs.add(tab, new HomePage(this));
|
||||
}
|
||||
|
||||
public void openTab(TabContainer container) {
|
||||
var tab = new Tab();
|
||||
var style = tab.getStyle();
|
||||
style.setPaddingTop("0rem");
|
||||
style.setPaddingBottom("0rem");
|
||||
tab.setLabel(container.getTitle());
|
||||
container.setTab(tab);
|
||||
|
||||
var closeTabButton = new Span(VaadinIcon.CLOSE_SMALL.create());
|
||||
closeTabButton.getStyle().setMarginLeft("1rem");
|
||||
closeTabButton.addClickListener(event -> tabs.remove(tab));
|
||||
tab.add(closeTabButton);
|
||||
|
||||
tabs.add(tab, container);
|
||||
tabs.setSelectedTab(tab);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package ru.dragonestia.editor.page.browser;
|
||||
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.tabs.Tab;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class TabContainer extends VerticalLayout {
|
||||
|
||||
protected final BrowserPage browser;
|
||||
private String title;
|
||||
private Tab tab;
|
||||
|
||||
public TabContainer(BrowserPage browser, String title) {
|
||||
this.browser = browser;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
tab.setLabel(title);
|
||||
}
|
||||
}
|
||||
@ -14,5 +14,8 @@ spring:
|
||||
hibernate:
|
||||
ddl-auto: validate
|
||||
|
||||
mvc:
|
||||
static-path-pattern: '/static/**'
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user