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 net.minestom.server.event.player.PlayerChatEvent;
|
||||||
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
||||||
import ru.dragonestia.msb3.api.module.MotdModule;
|
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.module.ResourcePackRepositoryModule;
|
||||||
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
import ru.dragonestia.msb3.api.talk.dialogue.DialogueRenderer;
|
import ru.dragonestia.msb3.api.talk.dialogue.DialogueRenderer;
|
||||||
import ru.dragonestia.msb3.api.talk.dialogue.DialogueTheme;
|
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 team.unnamed.creative.ResourcePack;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class DefaultBootstrap extends ServerInitializer {
|
public class DefaultBootstrap extends ServerInitializer {
|
||||||
|
|
||||||
@ -23,56 +28,58 @@ public class DefaultBootstrap extends ServerInitializer {
|
|||||||
public void onDefaultModulesLoaded() {
|
public void onDefaultModulesLoaded() {
|
||||||
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
|
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
|
||||||
FlatWorldModule.init(GameMode.ADVENTURE);
|
FlatWorldModule.init(GameMode.ADVENTURE);
|
||||||
|
PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500));
|
||||||
|
|
||||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
|
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
|
||||||
var player = event.getPlayer();
|
var player = event.getPlayer();
|
||||||
|
|
||||||
var render = new DialogueRenderer(player, DialogueTheme.builder().build());
|
PictureBanner.TEST.show(player);
|
||||||
render.setText("""
|
// var render = new DialogueRenderer(player, DialogueTheme.builder().build());
|
||||||
Абсолютно точно.
|
// render.setText("""
|
||||||
Я знаю точнo - невозможное возможно
|
// Абсолютно точно.
|
||||||
Сойти с ума, влюбиться так неосторoжно
|
// Я знаю точн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_1, "Всем привет!", ctx -> {});
|
||||||
render.setButton(ButtonNumber.BUTTON_3, "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nZaebumba", ctx -> {});
|
// render.setButton(ButtonNumber.BUTTON_2, "I am a teapot", ctx -> {});
|
||||||
render.setButton(ButtonNumber.BUTTON_4, "msb3 is top!", 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();
|
//
|
||||||
|
// render.show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeResources(ResourcePack resourcePack) {
|
public void onInitializeResources(ResourcePack resourcePack) {
|
||||||
|
ClassPreLoader.preload(PictureBanner.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import lombok.experimental.UtilityClass;
|
|||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import ru.dragonestia.msb3.api.entity.PickableItem;
|
||||||
import ru.dragonestia.msb3.api.item.ItemUtil;
|
import ru.dragonestia.msb3.api.item.ItemUtil;
|
||||||
import ru.dragonestia.msb3.api.resource.DialogueResources;
|
import ru.dragonestia.msb3.api.resource.DialogueResources;
|
||||||
import ru.dragonestia.msb3.api.resource.MonologueResources;
|
import ru.dragonestia.msb3.api.resource.MonologueResources;
|
||||||
@ -77,6 +78,7 @@ public final class ServerBootstrap {
|
|||||||
|
|
||||||
private void initDefaultModules() {
|
private void initDefaultModules() {
|
||||||
ItemUtil.init();
|
ItemUtil.init();
|
||||||
|
PickableItem.registerEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compileResourcePack() {
|
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();
|
server.start();
|
||||||
|
|
||||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
|
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
|
||||||
|
if (!event.isFirstSpawn()) return;
|
||||||
|
|
||||||
var player = event.getPlayer();
|
var player = event.getPlayer();
|
||||||
|
|
||||||
player.sendResourcePacks(ResourcePackRequest.resourcePackRequest()
|
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.DynamicChunk;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
|
import net.minestom.server.instance.LightingChunk;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class OutOfBoundsChunk extends DynamicChunk {
|
public interface OutOfBoundsChunk {
|
||||||
|
|
||||||
public OutOfBoundsChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
|
class Dynamic extends DynamicChunk implements OutOfBoundsChunk {
|
||||||
super(instance, chunkX, chunkZ);
|
|
||||||
|
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(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
var sourceChunkData = source.provideChunk(chunkX, chunkZ);
|
var sourceChunkData = source.provideChunk(chunkX, chunkZ);
|
||||||
if (sourceChunkData.isEmpty()) {
|
if (sourceChunkData.isEmpty()) {
|
||||||
return new OutOfBoundsChunk(instance, chunkX, chunkZ);
|
return new OutOfBoundsChunk.Dynamic(instance, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
return new SharedChunk(instance, chunkX, chunkZ, sourceChunkData.get());
|
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.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class DialogueContext {
|
public class DialogueContext {
|
||||||
|
|
||||||
|
private String groupId;
|
||||||
private String id;
|
private String id;
|
||||||
private String text;
|
private String text;
|
||||||
private String comment;
|
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:
|
hibernate:
|
||||||
ddl-auto: validate
|
ddl-auto: validate
|
||||||
|
|
||||||
|
mvc:
|
||||||
|
static-path-pattern: '/static/**'
|
||||||
|
|
||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user