From aba9092635bd2420ecc296de170e34613666519f Mon Sep 17 00:00:00 2001 From: bubblobill Date: Sat, 17 Jan 2026 16:19:47 +0800 Subject: [PATCH 01/15] - Added a localisation parameter (defaults to true) to "toString" in ShapeDrawable and AbstractDrawing. - On FALSE it returns the Zone.Layer.name() instead of Zone.Layer.value() --- .../maptool/client/functions/ShapeFunctions.java | 2 +- .../rptools/maptool/model/drawing/AbstractDrawing.java | 10 +++++++++- .../rptools/maptool/model/drawing/ShapeDrawable.java | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java b/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java index f696e8ed56..51a9205058 100644 --- a/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java +++ b/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java @@ -614,7 +614,7 @@ private Object getProperties( seg, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], coords[6])); pi.next(); } - StringBuilder stringBuilder = new StringBuilder(sd.toString()); + StringBuilder stringBuilder = new StringBuilder(sd.toString(false)); stringBuilder.append("segments=").append(String.join(",", segments)).append(";"); if (delimiter.equalsIgnoreCase("json")) { diff --git a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java index de6357eebe..b2dcd35015 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java +++ b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java @@ -145,9 +145,17 @@ public int hashCode() { @Override public String toString() { + return toString(true); + } + + public String toString(boolean localised) { StringBuilder sb = new StringBuilder(); sb.append("name=").append(getName()).append(";"); - sb.append("layer=").append(getLayer()).append(";"); + if(localised) { + sb.append("layer=").append(getLayer()).append(";"); + } else { + sb.append("layer=").append(getLayer().name()).append(";"); + } sb.append("id=").append(getId()).append(";"); return sb.toString(); } diff --git a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java index 429d6eeb1f..d41053ec52 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java +++ b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java @@ -178,7 +178,10 @@ private Object applyAA(Graphics2D g) { @Override public String toString() { - StringBuilder sb = new StringBuilder(super.toString()); + return toString(true); + } + public String toString(boolean localised) { + StringBuilder sb = new StringBuilder(super.toString(localised)); sb.append("antiAliasing=").append(getUseAntiAliasing()).append(";"); sb.append("shapeType=").append(getShapeTypeName()).append(";"); sb.append("bounds=\""); From 81fe8eb11279b24033578bdae46d1ba3016f3c9a Mon Sep 17 00:00:00 2001 From: bubblobill Date: Sun, 18 Jan 2026 18:39:33 +0800 Subject: [PATCH 02/15] now with spotless --- .../java/net/rptools/maptool/model/drawing/AbstractDrawing.java | 2 +- .../java/net/rptools/maptool/model/drawing/ShapeDrawable.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java index b2dcd35015..3978957a35 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java +++ b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java @@ -151,7 +151,7 @@ public String toString() { public String toString(boolean localised) { StringBuilder sb = new StringBuilder(); sb.append("name=").append(getName()).append(";"); - if(localised) { + if (localised) { sb.append("layer=").append(getLayer()).append(";"); } else { sb.append("layer=").append(getLayer().name()).append(";"); diff --git a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java index d41053ec52..0a86406258 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java +++ b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java @@ -180,6 +180,7 @@ private Object applyAA(Graphics2D g) { public String toString() { return toString(true); } + public String toString(boolean localised) { StringBuilder sb = new StringBuilder(super.toString(localised)); sb.append("antiAliasing=").append(getUseAntiAliasing()).append(";"); From 7be75c5a73401eb7f833a81c9284fd9c67117e2a Mon Sep 17 00:00:00 2001 From: bubblobill Date: Mon, 19 Jan 2026 03:06:10 +0800 Subject: [PATCH 03/15] Added state and bar data to StatSheetContext --- .../model/sheet/stats/StatSheetContext.java | 96 +++++++++++++++++-- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index 1895823f0b..3258c4b730 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -15,15 +15,20 @@ package net.rptools.maptool.model.sheet.stats; import java.awt.Dimension; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; import net.rptools.lib.AwtUtil; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppPreferences; import net.rptools.maptool.client.AppUtil; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.MapToolVariableResolver; +import net.rptools.maptool.client.ui.token.AbstractTokenOverlay; +import net.rptools.maptool.client.ui.token.BarTokenOverlay; import net.rptools.maptool.model.Token; import net.rptools.maptool.model.player.Player; import net.rptools.maptool.util.HTMLUtil; @@ -139,6 +144,12 @@ public String getShortName() { /** The properties of the token. */ private final List properties = new ArrayList<>(); + /** The bars shown on the token. */ + private final List> bars = new ArrayList<>(); + + /** The states set on the token. */ + private final List> states = new ArrayList<>(); + /** The notes of the token. */ private final String notes; @@ -168,10 +179,21 @@ public String getShortName() { * @param location The location of the stat sheet. */ public StatSheetContext(Token token, Player player, StatSheetLocation location) { - + boolean playerOwns = AppUtil.playerOwns(token); name = token.getName(); tokenType = token.getType().name(); + List stateOverlayNames = new ArrayList<>(); + stateOverlayNames.addAll(MapTool.getCampaign().getTokenBarsMap().keySet()); + stateOverlayNames.addAll(MapTool.getCampaign().getTokenStatesMap().keySet()); + + for (String stateName : stateOverlayNames) { + Object stateValue = token.getState(stateName); + if (stateValue != null) { + addBarState(stateName, stateValue, playerOwns, player); + } + } + if (player.isGM()) { gmName = token.getGMName(); gmNotes = token.getGMNotes(); @@ -183,8 +205,8 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) gmNotesType = null; gm = false; } - notes = AppUtil.playerOwns(token) ? token.getNotes() : null; - notesType = AppUtil.playerOwns(token) ? token.getNotesType() : null; + notes = playerOwns ? token.getNotes() : null; + notesType = playerOwns ? token.getNotesType() : null; speechName = token.getSpeechName(); if (AppPreferences.showPortrait.get()) { @@ -206,7 +228,7 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) return; } - if (tp.isOwnerOnly() && !AppUtil.playerOwns(token)) { + if (tp.isOwnerOnly() && !playerOwns) { return; } @@ -255,6 +277,48 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }; } + private void addBarState(String stateName, Object stateValue, boolean playerOwns, Player player) { + AbstractTokenOverlay ato = null; + if (MapTool.getCampaign().getTokenBarsMap().containsKey(stateName)) { + ato = MapTool.getCampaign().getTokenBarsMap().get(stateName); + } else { + ato = MapTool.getCampaign().getTokenStatesMap().get(stateName); + } + if (ato.isShowOthers() || (playerOwns && ato.isShowOwner()) || player.isGM()) { + Map featureMap = new HashMap<>(); + featureMap.put("value", stateValue); + featureMap.put("type", ato.getClass().getSimpleName()); + String mName; + + try { + BeanInfo beanInfo = Introspector.getBeanInfo(ato.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + if (!"class".equals(propertyDescriptor.getName())) { + if (propertyDescriptor.getReadMethod().canAccess(ato)) { + mName = propertyDescriptor.getReadMethod().getName(); + if (mName.startsWith("is")) { + mName = mName.substring(2); + } else if (mName.startsWith("get") || mName.startsWith("has")) { + mName = mName.substring(3); + } + featureMap.put( + Introspector.decapitalize(mName), propertyDescriptor.getReadMethod().invoke(ato)); + } + } + } + } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + if (ato instanceof BarTokenOverlay) { + bars.add(featureMap); + } else { + states.add(featureMap); + } + } + } + /** * Returns the name of the token. * @@ -328,9 +392,9 @@ public List getProperties() { } /** - * Returns the css class for the location of the stat sheet. + * Returns the CSS class for the location of the stat sheet. * - * @return The css class for the location of the stat sheet. + * @return The CSS class for the location of the stat sheet. */ public String getStatSheetLocation() { return statSheetLocation; @@ -388,4 +452,18 @@ public String getTokenType() { public boolean isGm() { return gm; } + + /** + * @return States set on the token. + */ + public List> getStates() { + return states; + } + + /** + * @return Bars available on the token. + */ + public List> getBars() { + return bars; + } } From 21ca23a241f4854e588456f0f294128595d5d26e Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 20 Jan 2026 11:40:50 +0800 Subject: [PATCH 04/15] Added Jackson2 JSON helpers/resolvers for Handlebars --- gradle/libs.versions.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 353737ab63..edcd93213b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -94,6 +94,7 @@ jsvg = { group = "com.github.weisj", name = "jsvg", version = "1.4.0" } handlebars = { group = "com.github.jknack", name = "handlebars", version.ref = "handlebars" } handlebars-helpers = { group = "com.github.jknack", name = "handlebars-helpers", version.ref = "handlebars" } +handlebars-json = { group = "com.github.jknack", name = "handlebars-jackson2", version = "4.3.1" } # Apache commons and other utilities # parsing of configuration data @@ -213,7 +214,7 @@ flatlaf = [ "flatlaf-extras", "flatlaf-jide-oss", ] -handlebars = ["handlebars", "handlebars-helpers"] +handlebars = ["handlebars", "handlebars-helpers", "handlebars-json"] junit = ["junit-api", "junit-engine", "junit-params"] jai-imageio = ["jai-imageio-core", "jai-imageio-jpeg"] graalvm-js = ["graalvm-js", "graalvm-js-scriptengine"] From 9594f2a88905badddd00ee5856f081ba33327a50 Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 20 Jan 2026 11:47:25 +0800 Subject: [PATCH 05/15] Changes to StatSheetContext: 1. increased visibility of Property class to get rid of annoying warning 2. created local booleans "playerOwns" and "playerIsGm" in constructor to avoid repetitive lookups. 3. Added bar and state data to the class. --- .../model/sheet/stats/StatSheetContext.java | 137 ++++++++++++------ 1 file changed, 90 insertions(+), 47 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index 3258c4b730..5fba4abe52 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -14,13 +14,13 @@ */ package net.rptools.maptool.model.sheet.stats; -import java.awt.Dimension; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; +import java.awt.*; +import java.awt.image.BufferedImage; import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.text.Collator; import java.util.*; +import java.util.List; import net.rptools.lib.AwtUtil; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppPreferences; @@ -33,12 +33,14 @@ import net.rptools.maptool.model.player.Player; import net.rptools.maptool.util.HTMLUtil; import net.rptools.maptool.util.ImageManager; +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtilsBean; /** Class that extracts and provides the information needed to render a stat sheet. */ +@SuppressWarnings("unused") public class StatSheetContext { - /** Class that represents a token property on a stat sheet. */ - static class Property { + public static class Property { /** Name of the property. */ private final String name; @@ -61,7 +63,7 @@ static class Property { * @param displayName Display Name of the property. * @param value Value of the property. * @param gmOnly True if the property is GM only. - * @note GM only properties are only extracted if the player is a GM. + * @implNote GM only properties are only extracted if the player is a GM. */ Property(String name, String displayName, String shortName, Object value, boolean gmOnly) { this.name = name; @@ -180,21 +182,19 @@ public String getShortName() { */ public StatSheetContext(Token token, Player player, StatSheetLocation location) { boolean playerOwns = AppUtil.playerOwns(token); + boolean playerIsGm = player.isGM(); + name = token.getName(); tokenType = token.getType().name(); - List stateOverlayNames = new ArrayList<>(); - stateOverlayNames.addAll(MapTool.getCampaign().getTokenBarsMap().keySet()); - stateOverlayNames.addAll(MapTool.getCampaign().getTokenStatesMap().keySet()); - - for (String stateName : stateOverlayNames) { + for (String stateName : OVERLAY_NAMES) { Object stateValue = token.getState(stateName); if (stateValue != null) { - addBarState(stateName, stateValue, playerOwns, player); + addBarOrState(stateName, stateValue, playerOwns, playerIsGm); } } - if (player.isGM()) { + if (playerIsGm) { gmName = token.getGMName(); gmNotes = token.getGMNotes(); gmNotesType = token.getNotesType(); @@ -224,7 +224,7 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) .forEach( tp -> { if (tp.isShowOnStatSheet()) { - if (tp.isGMOnly() && !MapTool.getPlayer().isGM()) { + if (tp.isGMOnly() && !playerIsGm) { return; } @@ -237,10 +237,8 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) return; } - if (value instanceof String svalue) { - if (svalue.isBlank()) { - return; - } + if (value instanceof String sValue && sValue.isBlank()) { + return; } properties.add( new Property( @@ -253,13 +251,13 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }); Dimension dim; + BufferedImage image; if (token.getPortraitImage() != null) { - var image = ImageManager.getImage(token.getPortraitImage()); - dim = new Dimension(image.getWidth(), image.getHeight()); + image = ImageManager.getImage(token.getPortraitImage()); } else { - var image = ImageManager.getImage(token.getImageAssetId()); - dim = new Dimension(image.getWidth(), image.getHeight()); + image = ImageManager.getImage(token.getImageAssetId()); } + dim = new Dimension(image.getWidth(), image.getHeight()); AwtUtil.constrainTo(dim, AppPreferences.portraitSize.get()); portraitWidth = dim.width; portraitHeight = dim.height; @@ -277,41 +275,85 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }; } - private void addBarState(String stateName, Object stateValue, boolean playerOwns, Player player) { - AbstractTokenOverlay ato = null; + private static final Comparator> stateComparator = + (o1, o2) -> { + String _s1 = o1.get("group").toString(); + String _s2 = o2.get("group").toString(); + // for different groups use natural order by group value + int result = Collator.getInstance().compare(_s1, _s2); + if (result != 0) { + return result; + } + // for the same group, use the "order" value - should always be present + if (Objects.equals(_s1, _s2) + && o1.get("order") instanceof Integer _i1 + && o2.get("order") instanceof Integer _i2) { + return _i1.compareTo(_i2); + } + return 0; // should never reach this point + }; + private static final List BAR_NAMES = + new ArrayList<>(MapTool.getCampaign().getTokenBarsMap().keySet()); + private static final List STATE_NAMES = + new ArrayList<>(MapTool.getCampaign().getTokenStatesMap().keySet()); + private static final List OVERLAY_NAMES = + new ArrayList<>() { + { + addAll(BAR_NAMES); + addAll(STATE_NAMES); + } + }; + + private void addBarOrState( + String stateName, Object stateValue, boolean playerOwns, boolean playerIsGm) { + + AbstractTokenOverlay ato; if (MapTool.getCampaign().getTokenBarsMap().containsKey(stateName)) { ato = MapTool.getCampaign().getTokenBarsMap().get(stateName); } else { ato = MapTool.getCampaign().getTokenStatesMap().get(stateName); } - if (ato.isShowOthers() || (playerOwns && ato.isShowOwner()) || player.isGM()) { + if ((ato.isShowOthers() && !playerOwns) + || (playerOwns && ato.isShowOwner()) + || (playerIsGm && ato.isShowGM())) { Map featureMap = new HashMap<>(); - featureMap.put("value", stateValue); - featureMap.put("type", ato.getClass().getSimpleName()); + featureMap.put( + "type", + ato.getClass() + .getSimpleName() + .replaceAll("BarTokenOverlay", "") + .replaceAll("TokenOverlay", "")); + try { + PropertyUtilsBean pub = BeanUtilsBean.getInstance().getPropertyUtils(); + featureMap.putAll(pub.describe(ato)); + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw new RuntimeException(e); + } String mName; - try { - BeanInfo beanInfo = Introspector.getBeanInfo(ato.getClass()); - PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); - for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { - if (!"class".equals(propertyDescriptor.getName())) { - if (propertyDescriptor.getReadMethod().canAccess(ato)) { - mName = propertyDescriptor.getReadMethod().getName(); - if (mName.startsWith("is")) { - mName = mName.substring(2); - } else if (mName.startsWith("get") || mName.startsWith("has")) { - mName = mName.substring(3); - } - featureMap.put( - Introspector.decapitalize(mName), propertyDescriptor.getReadMethod().invoke(ato)); - } + for (Map.Entry entry : featureMap.entrySet()) { + Object value = entry.getValue(); + if (value instanceof Color color) { + featureMap.put( + entry.getKey(), + String.format( + "rgba(%d,%d,%d,%#.3f)", + color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 255f)); + } else if (value instanceof MD5Key id) { + featureMap.put(entry.getKey(), String.format("asset://%s", id)); + } else if (value instanceof MD5Key[] idArray) { + String[] strOut = new String[idArray.length]; + for (int i = 0; i < idArray.length; i++) { + strOut[i] = String.format("asset://%s", idArray[i].toString()); } + featureMap.put(entry.getKey(), strOut); } - } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); } - if (ato instanceof BarTokenOverlay) { + featureMap.put( + "value", stateValue instanceof BigDecimal bd ? bd.doubleValue() : stateValue); + featureMap.remove("group"); + featureMap.remove("order"); bars.add(featureMap); } else { states.add(featureMap); @@ -457,6 +499,7 @@ public boolean isGm() { * @return States set on the token. */ public List> getStates() { + states.sort(stateComparator); return states; } From 8c24e81d106303bc7a9cd9a258d4776886c2117d Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 20 Jan 2026 14:10:51 +0800 Subject: [PATCH 06/15] Added documentation for extra bits --- .../model/sheet/stats/StatSheetContext.java | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index 5fba4abe52..ad70cb3a5f 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -275,27 +275,34 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }; } + /** Comparator for sorting State Groups */ private static final Comparator> stateComparator = (o1, o2) -> { - String _s1 = o1.get("group").toString(); - String _s2 = o2.get("group").toString(); + String s1 = o1.get("group").toString(); + String s2 = o2.get("group").toString(); // for different groups use natural order by group value - int result = Collator.getInstance().compare(_s1, _s2); + int result = Collator.getInstance().compare(s1, s2); if (result != 0) { return result; } // for the same group, use the "order" value - should always be present - if (Objects.equals(_s1, _s2) - && o1.get("order") instanceof Integer _i1 - && o2.get("order") instanceof Integer _i2) { - return _i1.compareTo(_i2); + if (Objects.equals(s1, s2) + && o1.get("order") instanceof Integer i1 + && o2.get("order") instanceof Integer i2) { + return i1.compareTo(i2); } return 0; // should never reach this point }; + + /** List of available Bar names */ private static final List BAR_NAMES = new ArrayList<>(MapTool.getCampaign().getTokenBarsMap().keySet()); + + /** List of available State names */ private static final List STATE_NAMES = new ArrayList<>(MapTool.getCampaign().getTokenStatesMap().keySet()); + + /** Combined list of Bar and State names */ private static final List OVERLAY_NAMES = new ArrayList<>() { { @@ -304,14 +311,22 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) } }; + /** + * Method for filtering overlays and adding them to the appropriate data set + * + * @param overlayName Name of bar or state + * @param overlayValue Value attached to bar or state + * @param playerOwns Used for filtering what to display + * @param playerIsGm Used for filtering what to display + */ private void addBarOrState( - String stateName, Object stateValue, boolean playerOwns, boolean playerIsGm) { + String overlayName, Object overlayValue, boolean playerOwns, boolean playerIsGm) { AbstractTokenOverlay ato; - if (MapTool.getCampaign().getTokenBarsMap().containsKey(stateName)) { - ato = MapTool.getCampaign().getTokenBarsMap().get(stateName); + if (MapTool.getCampaign().getTokenBarsMap().containsKey(overlayName)) { + ato = MapTool.getCampaign().getTokenBarsMap().get(overlayName); } else { - ato = MapTool.getCampaign().getTokenStatesMap().get(stateName); + ato = MapTool.getCampaign().getTokenStatesMap().get(overlayName); } if ((ato.isShowOthers() && !playerOwns) || (playerOwns && ato.isShowOwner()) @@ -351,9 +366,9 @@ private void addBarOrState( } if (ato instanceof BarTokenOverlay) { featureMap.put( - "value", stateValue instanceof BigDecimal bd ? bd.doubleValue() : stateValue); - featureMap.remove("group"); - featureMap.remove("order"); + "value", overlayValue instanceof BigDecimal bd ? bd.doubleValue() : overlayValue); + featureMap.remove("group"); // does not apply to bars + featureMap.remove("order"); // does not apply to bars bars.add(featureMap); } else { states.add(featureMap); From e9202279d8207c8d5e3b94f1ae25a2c6e80b4eba Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 20 Jan 2026 14:27:10 +0800 Subject: [PATCH 07/15] - new Developer option EnableHandlebarsDebugging -Rearranged HandlebarsUtil to make getting Handlebars instance easier externally. - Created HBDebugUtil for creating the files in the log directory. - Created a couple of templates for formatting the output. --- .../maptool/client/DeveloperOptions.java | 6 + .../client/ui/sheet/stats/StatSheet.java | 25 +++- .../net/rptools/maptool/util/HBDebugUtil.java | 139 ++++++++++++++++++ .../rptools/maptool/util/HandlebarsUtil.java | 30 ++-- .../rptools/maptool/language/i18n.properties | 2 + .../builtin/debugTemplates/_data-as-json.hbs | 1 + .../builtin/debugTemplates/debug-template.hbs | 32 ++++ 7 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 src/main/java/net/rptools/maptool/util/HBDebugUtil.java create mode 100644 src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs create mode 100644 src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs diff --git a/src/main/java/net/rptools/maptool/client/DeveloperOptions.java b/src/main/java/net/rptools/maptool/client/DeveloperOptions.java index 298f965911..8e0edc797b 100644 --- a/src/main/java/net/rptools/maptool/client/DeveloperOptions.java +++ b/src/main/java/net/rptools/maptool/client/DeveloperOptions.java @@ -25,6 +25,12 @@ public class DeveloperOptions { new PreferenceStore(Preferences.userRoot().node(AppConstants.APP_NAME + "/dev")); public static final class Toggle { + public static final Preference EnableHandlebarsDebugging = + store.defineBoolean( + "enableHandlebarsDebugging", + "Preferences.developer.enableHandlebarsDebugging.label", + "Preferences.developer.enableHandlebarsDebugging.tooltip", + false); public static final Preference AutoSaveMeasuredInSeconds = store.defineBoolean( "autoSaveMeasuredInSeconds", diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java index 2ed423b840..429c43d4f8 100644 --- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java +++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java @@ -14,24 +14,37 @@ */ package net.rptools.maptool.client.ui.sheet.stats; +import com.github.jknack.handlebars.*; import java.io.IOException; import java.net.URL; +import java.util.*; import javafx.application.Platform; import net.rptools.maptool.client.AppConstants; +import net.rptools.maptool.client.DeveloperOptions; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.ui.htmlframe.HTMLContent; import net.rptools.maptool.model.Token; import net.rptools.maptool.model.sheet.stats.StatSheetContext; import net.rptools.maptool.model.sheet.stats.StatSheetLocation; +import net.rptools.maptool.util.HBDebugUtil; import net.rptools.maptool.util.HandlebarsUtil; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Class that represents a pop up stat sheet. */ public class StatSheet { - /** Object for logging messages. */ - private static final Logger log = LogManager.getLogger(StatSheet.class); + private static final Logger log = LoggerFactory.getLogger(StatSheet.class); + + private static final HBDebugUtil HBD; + + static { + HBDebugUtil hbd = null; + if (DeveloperOptions.Toggle.EnableHandlebarsDebugging.get()) { + hbd = new HBDebugUtil(); + } + HBD = hbd; + } /** * Sets the content for the stat sheet. The content is a HTML page that is rendered using the @@ -67,6 +80,10 @@ public void setContent(Token token, String content, URL entry, StatSheetLocation null); } }); + if (HBD != null) { + Platform.runLater( + () -> HBD.publish(statSheetContext, token, content, entry, output.getHtmlString())); + } } catch (IOException e) { MapTool.showError("msg.error.renderingStatSheet", e); } diff --git a/src/main/java/net/rptools/maptool/util/HBDebugUtil.java b/src/main/java/net/rptools/maptool/util/HBDebugUtil.java new file mode 100644 index 0000000000..0ca6860d5b --- /dev/null +++ b/src/main/java/net/rptools/maptool/util/HBDebugUtil.java @@ -0,0 +1,139 @@ +/* + * This software Copyright by the RPTools.net development team, and + * licensed under the Affero GPL Version 3 or, at your option, any later + * version. + * + * MapTool Source Code is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public + * License * along with this source Code. If not, please visit + * and specifically the Affero license + * text at . + */ +package net.rptools.maptool.util; + +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache; +import com.github.jknack.handlebars.cache.TemplateCache; +import com.github.jknack.handlebars.io.ClassPathTemplateLoader; +import com.github.jknack.handlebars.io.TemplateLoader; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.time.format.TextStyle; +import java.util.*; +import net.rptools.maptool.client.AppUtil; +import net.rptools.maptool.client.DeveloperOptions; +import net.rptools.maptool.model.Token; +import net.rptools.maptool.model.sheet.stats.StatSheetContext; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HBDebugUtil { + /** Object for logging messages. */ + private static final Logger log = LoggerFactory.getLogger(HBDebugUtil.class); + + private static final Set LOG_FILES = new HashSet<>(); + private static final String RESOURCE_PATH = "/net/rptools/maptool/library/builtin/debugTemplates"; + private static final List templateNames = List.of("debug-template", "_data-as-json"); + private Template template; + private URI resourceUri; + private static final Map CONTEXT_MAP = new HashMap<>(); + + private static final Path LOG_FOLDER = AppUtil.getAppHome("logs").getAbsoluteFile().toPath(); + + public HBDebugUtil() { + if (DeveloperOptions.Toggle.EnableHandlebarsDebugging.get()) { + try { + TemplateCache cache = new ConcurrentMapTemplateCache(); + TemplateLoader loader = new ClassPathTemplateLoader(RESOURCE_PATH); + Handlebars handlebars = + HandlebarsUtil.getHandlebarsInstance(loader) + .with(cache) + .setCharset(StandardCharsets.ISO_8859_1) + .prettyPrint(true); + + for (String fileName : templateNames) { + Template t = handlebars.compile(loader.sourceAt(fileName)); + if (fileName.endsWith(templateNames.getFirst())) { + template = t; + } + } + Runtime.getRuntime().addShutdownHook(new Thread(this::retract)); + + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + } + } + + public void publish( + StatSheetContext statSheetContext, Token token, String content, URL entry, String HTMLout) { + String day = + LocalDateTime.now() + .getDayOfWeek() + .getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault()); + String sheetName = entry.toString(); + sheetName = sheetName.substring(sheetName.lastIndexOf('/') + 1).replace(".hbs", ""); + String filePrefix = String.format("%s-%s-%s", day, sheetName, token.getName()); + + Path tempFileHbs = LOG_FOLDER.resolve(filePrefix + ".hbs"); + Path tempFileHtml = LOG_FOLDER.resolve(filePrefix + ".html"); + Path tempFileInfo = LOG_FOLDER.resolve(filePrefix + "-info.html"); + + try { + for (Path tmpFile : new Path[] {tempFileHbs, tempFileHtml, tempFileInfo}) { + Files.deleteIfExists(tmpFile); + LOG_FILES.remove(tmpFile); + } + + CONTEXT_MAP.clear(); + CONTEXT_MAP.put("token", token); + CONTEXT_MAP.put("sheetName", sheetName); + CONTEXT_MAP.put("templateString", content); + CONTEXT_MAP.put("templateURL", tempFileHbs.getFileName()); + CONTEXT_MAP.put("htmlOut", tempFileHtml.getFileName()); + Context context = Context.newBuilder(statSheetContext).combine(CONTEXT_MAP).build(); + + IOUtils.write( + template.apply(context), + Files.newOutputStream(tempFileInfo, StandardOpenOption.CREATE), + StandardCharsets.ISO_8859_1); + LOG_FILES.add(tempFileInfo); + IOUtils.write( + content, + Files.newOutputStream(tempFileHbs, StandardOpenOption.CREATE), + StandardCharsets.ISO_8859_1); + LOG_FILES.add(tempFileHbs); + IOUtils.write( + HTMLout, + Files.newOutputStream(tempFileHtml, StandardOpenOption.CREATE), + StandardCharsets.ISO_8859_1); + LOG_FILES.add(tempFileHtml); + + log.info("Handlebars debug output written to {}", LOG_FOLDER); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + } + + private void retract() { + for (Path p : LOG_FILES) { + try { + Files.deleteIfExists(p); + } catch (IOException e) { + log.error(e.getLocalizedMessage(), e); + } + } + } +} diff --git a/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java b/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java index 18d1ab83b2..5d219bb54c 100644 --- a/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java +++ b/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java @@ -14,11 +14,7 @@ */ package net.rptools.maptool.util; -import com.github.jknack.handlebars.Context; -import com.github.jknack.handlebars.Handlebars; -import com.github.jknack.handlebars.Helper; -import com.github.jknack.handlebars.Options; -import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.*; import com.github.jknack.handlebars.context.JavaBeanValueResolver; import com.github.jknack.handlebars.helper.ConditionalHelpers; import com.github.jknack.handlebars.helper.StringHelpers; @@ -40,6 +36,7 @@ import java.util.Arrays; import java.util.Base64; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import net.rptools.maptool.model.Token; import net.rptools.maptool.model.library.Library; import net.rptools.maptool.model.library.LibraryManager; @@ -52,6 +49,19 @@ * @param The type of the bean to apply the template to. */ public class HandlebarsUtil { + public static Handlebars getHandlebarsInstance(@Nullable TemplateLoader loader) { + Handlebars handlebars = new Handlebars(loader); + StringHelpers.register(handlebars); + Arrays.stream(ConditionalHelpers.values()).forEach(h -> handlebars.registerHelper(h.name(), h)); + handlebars.registerHelper("json", Jackson2Helper.INSTANCE); + NumberHelper.register(handlebars); + handlebars.registerHelper(AssignHelper.NAME, AssignHelper.INSTANCE); + handlebars.registerHelper(IncludeHelper.NAME, IncludeHelper.INSTANCE); + Arrays.stream(MapToolHelpers.values()).forEach(h -> handlebars.registerHelper(h.name(), h)); + + return handlebars; + } + public static boolean isAssetFileHandlebars(String filename) { if (filename == null) { return false; @@ -155,16 +165,8 @@ public Object apply(final Object context, final Options options) { * @throws IOException If there is an error compiling the template. */ private HandlebarsUtil(String stringTemplate, TemplateLoader loader) throws IOException { + Handlebars handlebars = getHandlebarsInstance(loader); try { - Handlebars handlebars = new Handlebars(loader); - StringHelpers.register(handlebars); - Arrays.stream(ConditionalHelpers.values()) - .forEach(h -> handlebars.registerHelper(h.name(), h)); - NumberHelper.register(handlebars); - handlebars.registerHelper(AssignHelper.NAME, AssignHelper.INSTANCE); - handlebars.registerHelper(IncludeHelper.NAME, IncludeHelper.INSTANCE); - Arrays.stream(MapToolHelpers.values()).forEach(h -> handlebars.registerHelper(h.name(), h)); - template = handlebars.compileInline(stringTemplate); } catch (IOException e) { log.error("Handlebars Error: {}", e.getMessage()); diff --git a/src/main/resources/net/rptools/maptool/language/i18n.properties b/src/main/resources/net/rptools/maptool/language/i18n.properties index eee02f0da2..bd3a0d7201 100644 --- a/src/main/resources/net/rptools/maptool/language/i18n.properties +++ b/src/main/resources/net/rptools/maptool/language/i18n.properties @@ -728,6 +728,8 @@ Preferences.developer.debugTokenDragging.tooltip = When enabled, highlights key Preferences.developer.enableLibGDXRendererToggleButton.label = Enable LibGDX toggle button Preferences.developer.enableLibGDXRendererToggleButton.tooltip = When enabled, adds a toggle button to the toolbar that allows toggling between the Swing-based renderer and the LibGD-based renderer. Preferences.developer.info.developerOptionsInUsePost = If this is not intended, go to {0} > {1} > {2} tab and disable the options there. +Preferences.developer.enableHandlebarsDebugging.label = Enable Handlebars Debugging output +Preferences.developer.enableHandlebarsDebugging.tooltip = Log the handlebars context data and the raw template string. Preferences.tab.interactions = Interactions Preferences.label.maps.fow = New maps have Fog of War Preferences.label.maps.fow.tooltip = Fog of War can be enabled or disabled on individual maps. diff --git a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs new file mode 100644 index 0000000000..8dc5240787 --- /dev/null +++ b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs @@ -0,0 +1 @@ +{{#json escapeHTML=true pretty=true}}{{@data.statSheetContext}}{{/json}} \ No newline at end of file diff --git a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs new file mode 100644 index 0000000000..a0f5621044 --- /dev/null +++ b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs @@ -0,0 +1,32 @@ + + + Sheet Debug + + + + + + + + + + + + + + + + + + + + + + + + +
Handlebars Debug Info
Sheet{{sheetName}}
Token Name{{token.name}}
Token ID{{token.id}}
Handlebarslink
HTML Resultlink
+

Passed Data

+
{{> _data-as-json}}
+ + \ No newline at end of file From b4456d33ffa6e871cdbadb83474f1b78cd5757ee Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 20 Jan 2026 14:44:40 +0800 Subject: [PATCH 08/15] Access change --- src/main/java/net/rptools/maptool/util/HandlebarsUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java b/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java index 5d219bb54c..ab2406e9d6 100644 --- a/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java +++ b/src/main/java/net/rptools/maptool/util/HandlebarsUtil.java @@ -49,7 +49,7 @@ * @param The type of the bean to apply the template to. */ public class HandlebarsUtil { - public static Handlebars getHandlebarsInstance(@Nullable TemplateLoader loader) { + static Handlebars getHandlebarsInstance(@Nullable TemplateLoader loader) { Handlebars handlebars = new Handlebars(loader); StringHelpers.register(handlebars); Arrays.stream(ConditionalHelpers.values()).forEach(h -> handlebars.registerHelper(h.name(), h)); From 6b0c11f4d49e1ace2991a08f08439d529d8c3e69 Mon Sep 17 00:00:00 2001 From: bubblobill Date: Wed, 21 Jan 2026 15:23:12 +0800 Subject: [PATCH 09/15] added aspect-ratio information for images --- .../model/sheet/stats/StatSheetContext.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index ad70cb3a5f..00399faaf7 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -21,6 +21,8 @@ import java.text.Collator; import java.util.*; import java.util.List; +import java.util.function.Function; + import net.rptools.lib.AwtUtil; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppPreferences; @@ -251,13 +253,11 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }); Dimension dim; - BufferedImage image; if (token.getPortraitImage() != null) { - image = ImageManager.getImage(token.getPortraitImage()); + dim = getImageDimensions.apply(token.getPortraitImage()); } else { - image = ImageManager.getImage(token.getImageAssetId()); + dim = getImageDimensions.apply(token.getImageAssetId()); } - dim = new Dimension(image.getWidth(), image.getHeight()); AwtUtil.constrainTo(dim, AppPreferences.portraitSize.get()); portraitWidth = dim.width; portraitHeight = dim.height; @@ -275,6 +275,10 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }; } + private static final Function getImageDimensions = md5Key -> { + BufferedImage image = ImageManager.getImage(md5Key); + return new Dimension(image.getWidth(), image.getHeight()); + }; /** Comparator for sorting State Groups */ private static final Comparator> stateComparator = (o1, o2) -> { @@ -345,7 +349,7 @@ private void addBarOrState( throw new RuntimeException(e); } String mName; - + Map aspectRatioMap = new HashMap<>(); for (Map.Entry entry : featureMap.entrySet()) { Object value = entry.getValue(); if (value instanceof Color color) { @@ -356,14 +360,21 @@ private void addBarOrState( color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() / 255f)); } else if (value instanceof MD5Key id) { featureMap.put(entry.getKey(), String.format("asset://%s", id)); + Dimension dim = getImageDimensions.apply(id); + aspectRatioMap.put(entry.getKey() + "AspectRatio", dim.getWidth()/dim.getHeight()); } else if (value instanceof MD5Key[] idArray) { String[] strOut = new String[idArray.length]; + double[] arOut = new double[idArray.length]; for (int i = 0; i < idArray.length; i++) { strOut[i] = String.format("asset://%s", idArray[i].toString()); + Dimension dim = getImageDimensions.apply(idArray[i]); + arOut[i] = dim.getWidth()/dim.getHeight(); } + aspectRatioMap.put(entry.getKey() + "AspectRatio", arOut); featureMap.put(entry.getKey(), strOut); } } + featureMap.putAll(aspectRatioMap); if (ato instanceof BarTokenOverlay) { featureMap.put( "value", overlayValue instanceof BigDecimal bd ? bd.doubleValue() : overlayValue); From ea7a288aeb0110183d9f75fc3909d21785416443 Mon Sep 17 00:00:00 2001 From: bubblobill Date: Wed, 21 Jan 2026 15:24:01 +0800 Subject: [PATCH 10/15] formatting --- .../model/sheet/stats/StatSheetContext.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index 00399faaf7..95b410e2c2 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -22,7 +22,6 @@ import java.util.*; import java.util.List; import java.util.function.Function; - import net.rptools.lib.AwtUtil; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppPreferences; @@ -275,10 +274,12 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) }; } - private static final Function getImageDimensions = md5Key -> { - BufferedImage image = ImageManager.getImage(md5Key); - return new Dimension(image.getWidth(), image.getHeight()); - }; + private static final Function getImageDimensions = + md5Key -> { + BufferedImage image = ImageManager.getImage(md5Key); + return new Dimension(image.getWidth(), image.getHeight()); + }; + /** Comparator for sorting State Groups */ private static final Comparator> stateComparator = (o1, o2) -> { @@ -361,14 +362,14 @@ private void addBarOrState( } else if (value instanceof MD5Key id) { featureMap.put(entry.getKey(), String.format("asset://%s", id)); Dimension dim = getImageDimensions.apply(id); - aspectRatioMap.put(entry.getKey() + "AspectRatio", dim.getWidth()/dim.getHeight()); + aspectRatioMap.put(entry.getKey() + "AspectRatio", dim.getWidth() / dim.getHeight()); } else if (value instanceof MD5Key[] idArray) { String[] strOut = new String[idArray.length]; double[] arOut = new double[idArray.length]; for (int i = 0; i < idArray.length; i++) { strOut[i] = String.format("asset://%s", idArray[i].toString()); Dimension dim = getImageDimensions.apply(idArray[i]); - arOut[i] = dim.getWidth()/dim.getHeight(); + arOut[i] = dim.getWidth() / dim.getHeight(); } aspectRatioMap.put(entry.getKey() + "AspectRatio", arOut); featureMap.put(entry.getKey(), strOut); From 3f5bab017b6677b82aa0b5401dec511d2f584cde Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 17 Mar 2026 23:05:32 +0800 Subject: [PATCH 11/15] Fixed character set in HBDebugUtil. Added null AbstractTokenOverlay protection to StatSheetContext. Inlined lookup of campaign states and bars to prevent changes to campaign not being included. --- .../model/sheet/stats/StatSheetContext.java | 28 ++++++++----------- .../net/rptools/maptool/util/HBDebugUtil.java | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index 95b410e2c2..f4abf0f933 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -22,6 +22,7 @@ import java.util.*; import java.util.List; import java.util.function.Function; +import java.util.stream.Stream; import net.rptools.lib.AwtUtil; import net.rptools.lib.MD5Key; import net.rptools.maptool.client.AppPreferences; @@ -188,6 +189,13 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) name = token.getName(); tokenType = token.getType().name(); + /* Combined list of Bar and State names */ + final List OVERLAY_NAMES = + Stream.concat( + MapTool.getCampaign().getTokenBarsMap().keySet().stream(), + MapTool.getCampaign().getTokenStatesMap().keySet().stream()) + .toList(); + for (String stateName : OVERLAY_NAMES) { Object stateValue = token.getState(stateName); if (stateValue != null) { @@ -299,23 +307,6 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) return 0; // should never reach this point }; - /** List of available Bar names */ - private static final List BAR_NAMES = - new ArrayList<>(MapTool.getCampaign().getTokenBarsMap().keySet()); - - /** List of available State names */ - private static final List STATE_NAMES = - new ArrayList<>(MapTool.getCampaign().getTokenStatesMap().keySet()); - - /** Combined list of Bar and State names */ - private static final List OVERLAY_NAMES = - new ArrayList<>() { - { - addAll(BAR_NAMES); - addAll(STATE_NAMES); - } - }; - /** * Method for filtering overlays and adding them to the appropriate data set * @@ -333,6 +324,9 @@ private void addBarOrState( } else { ato = MapTool.getCampaign().getTokenStatesMap().get(overlayName); } + if (ato == null) { + return; + } if ((ato.isShowOthers() && !playerOwns) || (playerOwns && ato.isShowOwner()) || (playerIsGm && ato.isShowGM())) { diff --git a/src/main/java/net/rptools/maptool/util/HBDebugUtil.java b/src/main/java/net/rptools/maptool/util/HBDebugUtil.java index 0ca6860d5b..6004a393de 100644 --- a/src/main/java/net/rptools/maptool/util/HBDebugUtil.java +++ b/src/main/java/net/rptools/maptool/util/HBDebugUtil.java @@ -60,7 +60,7 @@ public HBDebugUtil() { Handlebars handlebars = HandlebarsUtil.getHandlebarsInstance(loader) .with(cache) - .setCharset(StandardCharsets.ISO_8859_1) + .setCharset(StandardCharsets.UTF_8) .prettyPrint(true); for (String fileName : templateNames) { From 165059ef42667c223b8fbb66e25e65d3d430feab Mon Sep 17 00:00:00 2001 From: bubblobill Date: Tue, 17 Mar 2026 23:15:54 +0800 Subject: [PATCH 12/15] Conflict resolution --- .../maptool/client/functions/ShapeFunctions.java | 5 +---- .../maptool/model/drawing/AbstractDrawing.java | 16 ---------------- .../maptool/model/drawing/ShapeDrawable.java | 16 ---------------- 3 files changed, 1 insertion(+), 36 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java b/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java index a9cde69b35..65457004e2 100644 --- a/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java +++ b/src/main/java/net/rptools/maptool/client/functions/ShapeFunctions.java @@ -614,11 +614,8 @@ private Object getProperties( seg, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], coords[6])); pi.next(); } -<<<<<<< states_and_bars_on_stat_sheets - StringBuilder stringBuilder = new StringBuilder(sd.toString(false)); -======= + StringBuilder stringBuilder = new StringBuilder(sd.toNonLocalisedString()); ->>>>>>> develop stringBuilder.append("segments=").append(String.join(",", segments)).append(";"); if (delimiter.equalsIgnoreCase("json")) { diff --git a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java index c9db8bc4a8..31c662a051 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java +++ b/src/main/java/net/rptools/maptool/model/drawing/AbstractDrawing.java @@ -145,27 +145,11 @@ public int hashCode() { @Override public String toString() { -<<<<<<< states_and_bars_on_stat_sheets - return toString(true); - } - - public String toString(boolean localised) { - StringBuilder sb = new StringBuilder(); - sb.append("name=").append(getName()).append(";"); - if (localised) { - sb.append("layer=").append(getLayer()).append(";"); - } else { - sb.append("layer=").append(getLayer().name()).append(";"); - } - sb.append("id=").append(getId()).append(";"); - return sb.toString(); -======= return "name=" + getName() + ";" + "layer=" + getLayer() + ";" + "id=" + getId() + ";"; } public String toNonLocalisedString() { return "name=" + getName() + ";" + "layer=" + getLayer().name() + ";" + "id=" + getId() + ";"; ->>>>>>> develop } //// diff --git a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java index c47e0a9519..88a202bd61 100644 --- a/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java +++ b/src/main/java/net/rptools/maptool/model/drawing/ShapeDrawable.java @@ -178,21 +178,6 @@ private Object applyAA(Graphics2D g) { @Override public String toString() { -<<<<<<< states_and_bars_on_stat_sheets - return toString(true); - } - - public String toString(boolean localised) { - StringBuilder sb = new StringBuilder(super.toString(localised)); - sb.append("antiAliasing=").append(getUseAntiAliasing()).append(";"); - sb.append("shapeType=").append(getShapeTypeName()).append(";"); - sb.append("bounds=\""); - sb.append("x=").append(getBounds().x).append(";"); - sb.append("y=").append(getBounds().y).append(";"); - sb.append("width=").append(getBounds().width).append(";"); - sb.append("height=").append(getBounds().height).append("\";"); - return sb.toString(); -======= return super.toString() + "antiAliasing=" + getUseAntiAliasing() @@ -236,7 +221,6 @@ public String toNonLocalisedString() { + "height=" + getBounds().height + "\";"; ->>>>>>> develop } private void restoreAA(Graphics2D g, Object oldAA) { From c6bf42041a0b600a61b9a3e1dac3d97e7063169f Mon Sep 17 00:00:00 2001 From: bubblobill Date: Thu, 19 Mar 2026 00:35:42 +0800 Subject: [PATCH 13/15] Add isAltDown to SwingUtil. Add altDown to TokenHoverEnter/Exit. Add TokenHoverEnter event to StatSheetContext. Allow sheets to be displayed for NPCs. --- .../client/events/TokenHoverEnter.java | 4 +++- .../maptool/client/events/TokenHoverExit.java | 4 +++- .../maptool/client/swing/SwingUtil.java | 16 ++++++++++++++ .../maptool/client/tool/PointerTool.java | 9 +++++--- .../client/ui/sheet/stats/StatSheet.java | 13 +++++++----- .../ui/sheet/stats/StatSheetListener.java | 4 ++-- .../model/sheet/stats/StatSheetContext.java | 21 ++++++++++++++----- 7 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/events/TokenHoverEnter.java b/src/main/java/net/rptools/maptool/client/events/TokenHoverEnter.java index 34c73d74db..644710d53b 100644 --- a/src/main/java/net/rptools/maptool/client/events/TokenHoverEnter.java +++ b/src/main/java/net/rptools/maptool/client/events/TokenHoverEnter.java @@ -24,5 +24,7 @@ * @param zone the zone that the token is in. * @param shiftDown is the shift key down. * @param controlDown is the control key down. + * @param altDown is the alt-key down. */ -public record TokenHoverEnter(Token token, Zone zone, boolean shiftDown, boolean controlDown) {} +public record TokenHoverEnter( + Token token, Zone zone, boolean shiftDown, boolean controlDown, boolean altDown) {} diff --git a/src/main/java/net/rptools/maptool/client/events/TokenHoverExit.java b/src/main/java/net/rptools/maptool/client/events/TokenHoverExit.java index 12a18fd132..a17cc670e9 100644 --- a/src/main/java/net/rptools/maptool/client/events/TokenHoverExit.java +++ b/src/main/java/net/rptools/maptool/client/events/TokenHoverExit.java @@ -24,5 +24,7 @@ * @param zone the zone for the event. * @param shiftDown is the shift key down. * @param controlDown is the control key down. + * @param altDown is the alt-key down. */ -public record TokenHoverExit(Token token, Zone zone, boolean shiftDown, boolean controlDown) {} +public record TokenHoverExit( + Token token, Zone zone, boolean shiftDown, boolean controlDown, boolean altDown) {} diff --git a/src/main/java/net/rptools/maptool/client/swing/SwingUtil.java b/src/main/java/net/rptools/maptool/client/swing/SwingUtil.java index 3f51a03de2..24fc96c23f 100644 --- a/src/main/java/net/rptools/maptool/client/swing/SwingUtil.java +++ b/src/main/java/net/rptools/maptool/client/swing/SwingUtil.java @@ -108,6 +108,22 @@ public static boolean isShiftDown(int modifiers) { return (modifiers & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK; } + public static boolean isAltDown(InputEvent e) { + return isAltDown(e.getModifiersEx()); + } + + /** + * Passed the event's extended modifiers this method returns true if the Alt key, + * Right Alt key or Alt-Graph key is down. + * + * @param modifiers as returned by {@link InputEvent#getModifiersEx()} + * @return true if Alt/Right-Alt/Alt-Graph key is down + */ + public static boolean isAltDown(int modifiers) { + return (modifiers & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK + || (modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == InputEvent.ALT_GRAPH_DOWN_MASK; + } + /** * Centers the innerWindow over the outerWindow. Basically, this method finds the centerpoint of * the outerWindow and sets the location of innerWindow so that it's diff --git a/src/main/java/net/rptools/maptool/client/tool/PointerTool.java b/src/main/java/net/rptools/maptool/client/tool/PointerTool.java index 774a4e1f65..3d95863e4a 100644 --- a/src/main/java/net/rptools/maptool/client/tool/PointerTool.java +++ b/src/main/java/net/rptools/maptool/client/tool/PointerTool.java @@ -645,7 +645,8 @@ public void mouseMoved(MouseEvent e) { oldTokenUnderMouse, getZone(), SwingUtil.isShiftDown(keysDown), - SwingUtil.isControlDown(keysDown))); + SwingUtil.isControlDown(keysDown), + SwingUtil.isAltDown(keysDown))); } } else if (tokenUnderMouse != oldTokenUnderMouse) { statSheet = null; @@ -657,7 +658,8 @@ public void mouseMoved(MouseEvent e) { oldTokenUnderMouse, getZone(), SwingUtil.isShiftDown(keysDown), - SwingUtil.isControlDown(keysDown))); + SwingUtil.isControlDown(keysDown), + SwingUtil.isAltDown(keysDown))); } new MapToolEventBus() .getMainEventBus() @@ -666,7 +668,8 @@ public void mouseMoved(MouseEvent e) { tokenUnderMouse, getZone(), SwingUtil.isShiftDown(keysDown), - SwingUtil.isControlDown(keysDown))); + SwingUtil.isControlDown(keysDown), + SwingUtil.isAltDown(keysDown))); } Token marker = renderer.getMarkerAt(mouseX, mouseY); if (!AppUtil.tokenIsVisible(renderer.getZone(), marker, renderer.getPlayerView())) { diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java index 429c43d4f8..db6400a516 100644 --- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java +++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java @@ -22,8 +22,8 @@ import net.rptools.maptool.client.AppConstants; import net.rptools.maptool.client.DeveloperOptions; import net.rptools.maptool.client.MapTool; +import net.rptools.maptool.client.events.TokenHoverEnter; import net.rptools.maptool.client.ui.htmlframe.HTMLContent; -import net.rptools.maptool.model.Token; import net.rptools.maptool.model.sheet.stats.StatSheetContext; import net.rptools.maptool.model.sheet.stats.StatSheetLocation; import net.rptools.maptool.util.HBDebugUtil; @@ -50,13 +50,14 @@ public class StatSheet { * Sets the content for the stat sheet. The content is a HTML page that is rendered using the * Handlebars template engine. * - * @param token the token to render the stat sheet for. + * @param event the token hover event triggering the stat-sheet rendering. * @param content the content of the stat sheet. * @param location the location of the stat sheet. */ - public void setContent(Token token, String content, URL entry, StatSheetLocation location) { + public void setContent( + TokenHoverEnter event, String content, URL entry, StatSheetLocation location) { try { - var statSheetContext = new StatSheetContext(token, MapTool.getPlayer(), location); + var statSheetContext = new StatSheetContext(event, MapTool.getPlayer(), location); var output = HTMLContent.htmlFromString(new HandlebarsUtil<>(content, entry).apply(statSheetContext)) .injectURLBase(entry); @@ -82,7 +83,9 @@ public void setContent(Token token, String content, URL entry, StatSheetLocation }); if (HBD != null) { Platform.runLater( - () -> HBD.publish(statSheetContext, token, content, entry, output.getHtmlString())); + () -> + HBD.publish( + statSheetContext, event.token(), content, entry, output.getHtmlString())); } } catch (IOException e) { MapTool.showError("msg.error.renderingStatSheet", e); diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java index 9e7c001e03..c39201e107 100644 --- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java +++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheetListener.java @@ -58,9 +58,9 @@ public void onHoverEnter(TokenHoverEnter event) { var token = event.token(); if (MapTool.getPlayer().isGM() || AppUtil.playerOwns(token) - || token.getType() != Type.NPC) { + || token.getType() == Type.NPC) { statSheet.setContent( - event.token(), + event, ssManager.getStatSheetContent(ssId), ssRecord.entry(), ssProperties.location()); diff --git a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java index f4abf0f933..e7d023822f 100644 --- a/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java +++ b/src/main/java/net/rptools/maptool/model/sheet/stats/StatSheetContext.java @@ -29,9 +29,9 @@ import net.rptools.maptool.client.AppUtil; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.MapToolVariableResolver; +import net.rptools.maptool.client.events.TokenHoverEnter; import net.rptools.maptool.client.ui.token.AbstractTokenOverlay; import net.rptools.maptool.client.ui.token.BarTokenOverlay; -import net.rptools.maptool.model.Token; import net.rptools.maptool.model.player.Player; import net.rptools.maptool.util.HTMLUtil; import net.rptools.maptool.util.ImageManager; @@ -136,6 +136,9 @@ public String getShortName() { /** The portrait asset of the token. */ private final MD5Key portraitAsset; + /** The handout asset of the token. */ + private final MD5Key handoutAsset; + /** The width of the portrait on the stat sheet. */ private final int portraitWidth; @@ -175,16 +178,22 @@ public String getShortName() { /** True if the player is a GM. */ private final boolean gm; + /** Hover event */ + private final TokenHoverEnter event; + /** * Creates a new instance of the class. * - * @param token The token to extract the information from. + * @param hoverEvent the token hover event to build the stat-sheet for. * @param player The player to extract the information for. * @param location The location of the stat sheet. */ - public StatSheetContext(Token token, Player player, StatSheetLocation location) { - boolean playerOwns = AppUtil.playerOwns(token); - boolean playerIsGm = player.isGM(); + public StatSheetContext(TokenHoverEnter hoverEvent, Player player, StatSheetLocation location) { + this.event = hoverEvent; + var token = event.token(); + + final boolean playerOwns = AppUtil.playerOwns(token); + final boolean playerIsGm = player.isGM(); name = token.getName(); tokenType = token.getType().name(); @@ -218,6 +227,8 @@ public StatSheetContext(Token token, Player player, StatSheetLocation location) notesType = playerOwns ? token.getNotesType() : null; speechName = token.getSpeechName(); + handoutAsset = token.getCharsheetImage(); + if (AppPreferences.showPortrait.get()) { imageAsset = token.getImageAssetId(); portraitAsset = token.getPortraitImage(); From 55e87885eb984994457e084c8e63f2ab5e4b9131 Mon Sep 17 00:00:00 2001 From: bubblobill Date: Fri, 20 Mar 2026 21:58:02 +0800 Subject: [PATCH 14/15] Removed Handlebars debugging utility --- .../maptool/client/DeveloperOptions.java | 6 - .../client/ui/sheet/stats/StatSheet.java | 17 --- .../net/rptools/maptool/util/HBDebugUtil.java | 139 ------------------ .../rptools/maptool/language/i18n.properties | 2 - .../builtin/debugTemplates/_data-as-json.hbs | 1 - .../builtin/debugTemplates/debug-template.hbs | 32 ---- 6 files changed, 197 deletions(-) delete mode 100644 src/main/java/net/rptools/maptool/util/HBDebugUtil.java delete mode 100644 src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs delete mode 100644 src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs diff --git a/src/main/java/net/rptools/maptool/client/DeveloperOptions.java b/src/main/java/net/rptools/maptool/client/DeveloperOptions.java index 8e0edc797b..298f965911 100644 --- a/src/main/java/net/rptools/maptool/client/DeveloperOptions.java +++ b/src/main/java/net/rptools/maptool/client/DeveloperOptions.java @@ -25,12 +25,6 @@ public class DeveloperOptions { new PreferenceStore(Preferences.userRoot().node(AppConstants.APP_NAME + "/dev")); public static final class Toggle { - public static final Preference EnableHandlebarsDebugging = - store.defineBoolean( - "enableHandlebarsDebugging", - "Preferences.developer.enableHandlebarsDebugging.label", - "Preferences.developer.enableHandlebarsDebugging.tooltip", - false); public static final Preference AutoSaveMeasuredInSeconds = store.defineBoolean( "autoSaveMeasuredInSeconds", diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java index db6400a516..e16628c55e 100644 --- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java +++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java @@ -26,7 +26,6 @@ import net.rptools.maptool.client.ui.htmlframe.HTMLContent; import net.rptools.maptool.model.sheet.stats.StatSheetContext; import net.rptools.maptool.model.sheet.stats.StatSheetLocation; -import net.rptools.maptool.util.HBDebugUtil; import net.rptools.maptool.util.HandlebarsUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,16 +35,6 @@ public class StatSheet { /** Object for logging messages. */ private static final Logger log = LoggerFactory.getLogger(StatSheet.class); - private static final HBDebugUtil HBD; - - static { - HBDebugUtil hbd = null; - if (DeveloperOptions.Toggle.EnableHandlebarsDebugging.get()) { - hbd = new HBDebugUtil(); - } - HBD = hbd; - } - /** * Sets the content for the stat sheet. The content is a HTML page that is rendered using the * Handlebars template engine. @@ -81,12 +70,6 @@ public void setContent( null); } }); - if (HBD != null) { - Platform.runLater( - () -> - HBD.publish( - statSheetContext, event.token(), content, entry, output.getHtmlString())); - } } catch (IOException e) { MapTool.showError("msg.error.renderingStatSheet", e); } diff --git a/src/main/java/net/rptools/maptool/util/HBDebugUtil.java b/src/main/java/net/rptools/maptool/util/HBDebugUtil.java deleted file mode 100644 index 6004a393de..0000000000 --- a/src/main/java/net/rptools/maptool/util/HBDebugUtil.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This software Copyright by the RPTools.net development team, and - * licensed under the Affero GPL Version 3 or, at your option, any later - * version. - * - * MapTool Source Code is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public - * License * along with this source Code. If not, please visit - * and specifically the Affero license - * text at . - */ -package net.rptools.maptool.util; - -import com.github.jknack.handlebars.Context; -import com.github.jknack.handlebars.Handlebars; -import com.github.jknack.handlebars.Template; -import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache; -import com.github.jknack.handlebars.cache.TemplateCache; -import com.github.jknack.handlebars.io.ClassPathTemplateLoader; -import com.github.jknack.handlebars.io.TemplateLoader; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; -import java.time.format.TextStyle; -import java.util.*; -import net.rptools.maptool.client.AppUtil; -import net.rptools.maptool.client.DeveloperOptions; -import net.rptools.maptool.model.Token; -import net.rptools.maptool.model.sheet.stats.StatSheetContext; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class HBDebugUtil { - /** Object for logging messages. */ - private static final Logger log = LoggerFactory.getLogger(HBDebugUtil.class); - - private static final Set LOG_FILES = new HashSet<>(); - private static final String RESOURCE_PATH = "/net/rptools/maptool/library/builtin/debugTemplates"; - private static final List templateNames = List.of("debug-template", "_data-as-json"); - private Template template; - private URI resourceUri; - private static final Map CONTEXT_MAP = new HashMap<>(); - - private static final Path LOG_FOLDER = AppUtil.getAppHome("logs").getAbsoluteFile().toPath(); - - public HBDebugUtil() { - if (DeveloperOptions.Toggle.EnableHandlebarsDebugging.get()) { - try { - TemplateCache cache = new ConcurrentMapTemplateCache(); - TemplateLoader loader = new ClassPathTemplateLoader(RESOURCE_PATH); - Handlebars handlebars = - HandlebarsUtil.getHandlebarsInstance(loader) - .with(cache) - .setCharset(StandardCharsets.UTF_8) - .prettyPrint(true); - - for (String fileName : templateNames) { - Template t = handlebars.compile(loader.sourceAt(fileName)); - if (fileName.endsWith(templateNames.getFirst())) { - template = t; - } - } - Runtime.getRuntime().addShutdownHook(new Thread(this::retract)); - - } catch (IOException e) { - log.error(e.getLocalizedMessage(), e); - } - } - } - - public void publish( - StatSheetContext statSheetContext, Token token, String content, URL entry, String HTMLout) { - String day = - LocalDateTime.now() - .getDayOfWeek() - .getDisplayName(TextStyle.SHORT_STANDALONE, Locale.getDefault()); - String sheetName = entry.toString(); - sheetName = sheetName.substring(sheetName.lastIndexOf('/') + 1).replace(".hbs", ""); - String filePrefix = String.format("%s-%s-%s", day, sheetName, token.getName()); - - Path tempFileHbs = LOG_FOLDER.resolve(filePrefix + ".hbs"); - Path tempFileHtml = LOG_FOLDER.resolve(filePrefix + ".html"); - Path tempFileInfo = LOG_FOLDER.resolve(filePrefix + "-info.html"); - - try { - for (Path tmpFile : new Path[] {tempFileHbs, tempFileHtml, tempFileInfo}) { - Files.deleteIfExists(tmpFile); - LOG_FILES.remove(tmpFile); - } - - CONTEXT_MAP.clear(); - CONTEXT_MAP.put("token", token); - CONTEXT_MAP.put("sheetName", sheetName); - CONTEXT_MAP.put("templateString", content); - CONTEXT_MAP.put("templateURL", tempFileHbs.getFileName()); - CONTEXT_MAP.put("htmlOut", tempFileHtml.getFileName()); - Context context = Context.newBuilder(statSheetContext).combine(CONTEXT_MAP).build(); - - IOUtils.write( - template.apply(context), - Files.newOutputStream(tempFileInfo, StandardOpenOption.CREATE), - StandardCharsets.ISO_8859_1); - LOG_FILES.add(tempFileInfo); - IOUtils.write( - content, - Files.newOutputStream(tempFileHbs, StandardOpenOption.CREATE), - StandardCharsets.ISO_8859_1); - LOG_FILES.add(tempFileHbs); - IOUtils.write( - HTMLout, - Files.newOutputStream(tempFileHtml, StandardOpenOption.CREATE), - StandardCharsets.ISO_8859_1); - LOG_FILES.add(tempFileHtml); - - log.info("Handlebars debug output written to {}", LOG_FOLDER); - } catch (IOException e) { - log.error(e.getLocalizedMessage(), e); - } - } - - private void retract() { - for (Path p : LOG_FILES) { - try { - Files.deleteIfExists(p); - } catch (IOException e) { - log.error(e.getLocalizedMessage(), e); - } - } - } -} diff --git a/src/main/resources/net/rptools/maptool/language/i18n.properties b/src/main/resources/net/rptools/maptool/language/i18n.properties index bd3a0d7201..eee02f0da2 100644 --- a/src/main/resources/net/rptools/maptool/language/i18n.properties +++ b/src/main/resources/net/rptools/maptool/language/i18n.properties @@ -728,8 +728,6 @@ Preferences.developer.debugTokenDragging.tooltip = When enabled, highlights key Preferences.developer.enableLibGDXRendererToggleButton.label = Enable LibGDX toggle button Preferences.developer.enableLibGDXRendererToggleButton.tooltip = When enabled, adds a toggle button to the toolbar that allows toggling between the Swing-based renderer and the LibGD-based renderer. Preferences.developer.info.developerOptionsInUsePost = If this is not intended, go to {0} > {1} > {2} tab and disable the options there. -Preferences.developer.enableHandlebarsDebugging.label = Enable Handlebars Debugging output -Preferences.developer.enableHandlebarsDebugging.tooltip = Log the handlebars context data and the raw template string. Preferences.tab.interactions = Interactions Preferences.label.maps.fow = New maps have Fog of War Preferences.label.maps.fow.tooltip = Fog of War can be enabled or disabled on individual maps. diff --git a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs deleted file mode 100644 index 8dc5240787..0000000000 --- a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/_data-as-json.hbs +++ /dev/null @@ -1 +0,0 @@ -{{#json escapeHTML=true pretty=true}}{{@data.statSheetContext}}{{/json}} \ No newline at end of file diff --git a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs b/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs deleted file mode 100644 index a0f5621044..0000000000 --- a/src/main/resources/net/rptools/maptool/library/builtin/debugTemplates/debug-template.hbs +++ /dev/null @@ -1,32 +0,0 @@ - - - Sheet Debug - - - - - - - - - - - - - - - - - - - - - - - - -
Handlebars Debug Info
Sheet{{sheetName}}
Token Name{{token.name}}
Token ID{{token.id}}
Handlebarslink
HTML Resultlink
-

Passed Data

-
{{> _data-as-json}}
- - \ No newline at end of file From 0493dadede987aadefe5d64bf3918dd0cf9dec01 Mon Sep 17 00:00:00 2001 From: bubblobill Date: Fri, 20 Mar 2026 22:00:33 +0800 Subject: [PATCH 15/15] formatting --- .../net/rptools/maptool/client/ui/sheet/stats/StatSheet.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java index e16628c55e..296fce4286 100644 --- a/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java +++ b/src/main/java/net/rptools/maptool/client/ui/sheet/stats/StatSheet.java @@ -20,7 +20,6 @@ import java.util.*; import javafx.application.Platform; import net.rptools.maptool.client.AppConstants; -import net.rptools.maptool.client.DeveloperOptions; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.events.TokenHoverEnter; import net.rptools.maptool.client.ui.htmlframe.HTMLContent;