diff --git a/src/main/java/de/extio/game_engine/renderer/g2d/control/impl/G2DButtonControlImpl.java b/src/main/java/de/extio/game_engine/renderer/g2d/control/impl/G2DButtonControlImpl.java index 7877326..c6903fc 100644 --- a/src/main/java/de/extio/game_engine/renderer/g2d/control/impl/G2DButtonControlImpl.java +++ b/src/main/java/de/extio/game_engine/renderer/g2d/control/impl/G2DButtonControlImpl.java @@ -9,8 +9,11 @@ import de.extio.game_engine.renderer.g2d.theme.G2DThemeManager; import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonControl; import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ButtonData; import de.extio.game_engine.renderer.model.color.RgbaColor; +import de.extio.game_engine.renderer.model.event.MouseEnterEvent; +import de.extio.game_engine.renderer.model.event.MouseExitEvent; import de.extio.game_engine.renderer.model.event.UiControlEvent; import de.extio.game_engine.resource.StaticResource; +import de.extio.game_engine.spatial2.model.ImmutableCoordI2; public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonControl { @@ -23,6 +26,8 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo protected long lastControlDataUpdateTime; protected long lastControlDataUpdateTimeInternal; + + protected boolean hovered; @Override public void setCustomData(final ButtonData data) { @@ -68,7 +73,21 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo this.control.setLocation(this.x, this.y); this.positionModified = false; } - this.tooltipMousePosition = this.control.getLastMousePosition(); + final var mousePosition = this.control.getLastMousePosition(); + final var hoveredNow = mousePosition != null && this.visible && this.enabled; + if (hoveredNow != this.hovered) { + this.hovered = hoveredNow; + final var coord = mousePosition != null + ? ImmutableCoordI2.create(this.x + mousePosition.getX(), this.y + mousePosition.getY()) + : ImmutableCoordI2.create(this.x + this.width / 2, this.y + this.height / 2); + if (hoveredNow) { + this.rendererData.getEventService().fire(new MouseEnterEvent(this.id, 0, coord, coord)); + } + else { + this.rendererData.getEventService().fire(new MouseExitEvent(this.id, 0, coord, coord)); + } + } + this.tooltipMousePosition = mousePosition; super.render(); } @@ -82,6 +101,7 @@ public class G2DButtonControlImpl extends G2DBaseControlImpl implements ButtonCo this.control.invalidate(); this.control.close(); ((G2DRenderer) this.rendererData.getRenderer()).getMainFrame().remove(this.control); + this.hovered = false; super.close(); } diff --git a/src/main/java/de/extio/game_engine/renderer/model/event/MouseEnterEvent.java b/src/main/java/de/extio/game_engine/renderer/model/event/MouseEnterEvent.java index 76d943e..d3bacd0 100644 --- a/src/main/java/de/extio/game_engine/renderer/model/event/MouseEnterEvent.java +++ b/src/main/java/de/extio/game_engine/renderer/model/event/MouseEnterEvent.java @@ -7,8 +7,22 @@ import de.extio.game_engine.spatial2.model.CoordI2; */ public final class MouseEnterEvent extends MouseEvent { + private final String controlId; + public MouseEnterEvent(final int modifiers, final CoordI2 rawCoord, final CoordI2 scaledCoord) { + this(null, modifiers, rawCoord, scaledCoord); + } + + public MouseEnterEvent(final String controlId, final int modifiers, final CoordI2 rawCoord, final CoordI2 scaledCoord) { super(rawCoord, scaledCoord, modifiers); + this.controlId = controlId; + } + + /** + * Control ID the mouse entered, null if the mouse entered the viewport + */ + public String getControlId() { + return this.controlId; } } diff --git a/src/main/java/de/extio/game_engine/renderer/model/event/MouseExitEvent.java b/src/main/java/de/extio/game_engine/renderer/model/event/MouseExitEvent.java index 09d6fa7..50a9f50 100644 --- a/src/main/java/de/extio/game_engine/renderer/model/event/MouseExitEvent.java +++ b/src/main/java/de/extio/game_engine/renderer/model/event/MouseExitEvent.java @@ -7,8 +7,22 @@ import de.extio.game_engine.spatial2.model.CoordI2; */ public final class MouseExitEvent extends MouseEvent { + private final String controlId; + public MouseExitEvent(final int modifiers, final CoordI2 rawCoord, final CoordI2 scaledCoord) { + this(null, modifiers, rawCoord, scaledCoord); + } + + public MouseExitEvent(final String controlId, final int modifiers, final CoordI2 rawCoord, final CoordI2 scaledCoord) { super(rawCoord, scaledCoord, modifiers); + this.controlId = controlId; + } + + /** + * Control ID the mouse exited, null if the mouse exited the viewport + */ + public String getControlId() { + return this.controlId; } } diff --git a/src/main/java/de/extio/game_engine/storage/dialog/FileSelectionDialogModule.java b/src/main/java/de/extio/game_engine/storage/dialog/FileSelectionDialogModule.java index af29590..23b0891 100644 --- a/src/main/java/de/extio/game_engine/storage/dialog/FileSelectionDialogModule.java +++ b/src/main/java/de/extio/game_engine/storage/dialog/FileSelectionDialogModule.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.function.Function; import org.springframework.context.ApplicationContext; @@ -30,6 +31,8 @@ import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonCon import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.ToggleButtonData; import de.extio.game_engine.renderer.model.bo.DrawFontRenderingBo; import de.extio.game_engine.renderer.model.bo.HorizontalAlignment; +import de.extio.game_engine.renderer.model.event.MouseEnterEvent; +import de.extio.game_engine.renderer.model.event.MouseExitEvent; import de.extio.game_engine.renderer.model.event.UiControlEvent; import de.extio.game_engine.renderer.work.RenderingBoPool; import de.extio.game_engine.spatial2.model.Area2; @@ -145,8 +148,12 @@ public class FileSelectionDialogModule extends AbstractClientModule { private String nameFieldValue; + private Function tooltipDataProvider; + private final Map> folderPathByControlId = new HashMap<>(); + private final Map tooltipCache = new HashMap<>(); + private final Map itemByControlId = new HashMap<>(); private final Set> virtualFolders = new HashSet<>(); @@ -188,6 +195,8 @@ public class FileSelectionDialogModule extends AbstractClientModule { @Override public void onShow() { this.eventService.register(UiControlEvent.class, this.getId(), this::onUiControlEvent); + this.eventService.register(MouseEnterEvent.class, this.getId(), this::onItemMouseEnter); + this.eventService.register(MouseExitEvent.class, this.getId(), this::onItemMouseExit); this.eventService.register(FileSelectionMoveDialogResponse.class, this.getId(), this::onMoveResponse); this.eventService.register(FileSelectionNewFolderDialogResponse.class, this.getId(), this::onNewFolderResponse); this.getModuleService().changeDisplayState(this.dialogWindow.getId(), true); @@ -199,6 +208,8 @@ public class FileSelectionDialogModule extends AbstractClientModule { @Override public void onHide() { this.eventService.unregister(UiControlEvent.class, this.getId()); + this.eventService.unregister(MouseEnterEvent.class, this.getId()); + this.eventService.unregister(MouseExitEvent.class, this.getId()); this.eventService.unregister(FileSelectionMoveDialogResponse.class, this.getId()); this.eventService.unregister(FileSelectionNewFolderDialogResponse.class, this.getId()); this.getModuleService().changeDisplayState(this.dialogWindow.getId(), false); @@ -209,6 +220,10 @@ public class FileSelectionDialogModule extends AbstractClientModule { } public void open(final String requestId, final FileSelectionMode mode, final List basePath, final String filterPattern, final boolean recursive, final boolean modal, final String initialName, final Window parentWindow) { + this.open(requestId, mode, basePath, filterPattern, recursive, modal, initialName, parentWindow, null); + } + + public void open(final String requestId, final FileSelectionMode mode, final List basePath, final String filterPattern, final boolean recursive, final boolean modal, final String initialName, final Window parentWindow, final Function tooltipDataProvider) { this.requestId = requestId; this.mode = mode != null ? mode : FileSelectionMode.LOAD; this.basePath = basePath != null ? List.copyOf(basePath) : List.of(); @@ -216,6 +231,8 @@ public class FileSelectionDialogModule extends AbstractClientModule { this.filterPattern = filterPattern; this.recursive = recursive; this.modal = modal; + this.tooltipDataProvider = tooltipDataProvider; + this.tooltipCache.clear(); this.selectedDescriptor = null; this.selectionIsNew = this.mode == FileSelectionMode.SAVE; this.nameFieldValue = initialName != null && !initialName.isBlank() ? initialName.trim() : ""; @@ -269,6 +286,52 @@ public class FileSelectionDialogModule extends AbstractClientModule { } } + private void onItemMouseEnter(final MouseEnterEvent event) { + if (this.tooltipDataProvider == null || event == null || event.getControlId() == null || !event.getControlId().startsWith(ITEM_PREFIX)) { + return; + } + final var descriptor = this.itemByControlId.get(event.getControlId()); + if (descriptor == null) { + return; + } + final var data = this.resolveTooltipData(descriptor); + if (data == null || data.isBlank()) { + return; + } + this.updateItemTooltip(event.getControlId(), data); + } + + private void onItemMouseExit(final MouseExitEvent event) { + } + + private String resolveTooltipData(final StorageItemDescriptor descriptor) { + final var cached = this.tooltipCache.get(descriptor.id()); + if (cached != null) { + return cached; + } + String data; + try { + data = this.tooltipDataProvider.apply(descriptor); + } + catch (final Exception ex) { + data = null; + } + if (data == null) { + data = ""; + } + this.tooltipCache.put(descriptor.id(), data); + return data; + } + + private void updateItemTooltip(final String controlId, final String data) { + final var bo = this.itemsScrollArea.getRenderingBo(controlId, ControlRenderingBo.class); + if (bo != null) { + bo.setTooltip(data); + this.itemsScrollArea.putRenderingBo(bo); + this.dialogWindow.draw(); + } + } + private void buildDialog() { this.dialogWindow.clearRenderingBos(); this.clearScrollArea(); @@ -685,6 +748,8 @@ public class FileSelectionDialogModule extends AbstractClientModule { this.selectedDescriptor = null; this.selectionIsNew = false; this.nameFieldValue = ""; + this.tooltipDataProvider = null; + this.tooltipCache.clear(); this.virtualFolders.clear(); this.folderPathByControlId.clear(); this.itemByControlId.clear();