From 1aad77f37c80de47db3845f1534d09823f82b2a3 Mon Sep 17 00:00:00 2001 From: Ed Reed Date: Sun, 25 Feb 2024 20:44:35 -0800 Subject: [PATCH] Improve preferences grid layout (#28) --- .../com/nrg948/annotations/Annotations.java | 1 - .../main/java/com/nrg948/package-info.java | 2 +- .../nrg948/preferences/RobotPreferences.java | 90 +++++++++++++++++-- .../preferences/RobotPreferencesLayout.java | 16 ++++ .../preferences/RobotPreferencesValue.java | 30 +++++++ .../com/nrg948/preferences/package-info.java | 2 +- 6 files changed, 130 insertions(+), 11 deletions(-) diff --git a/nrgcommon/src/main/java/com/nrg948/annotations/Annotations.java b/nrgcommon/src/main/java/com/nrg948/annotations/Annotations.java index 79081da..b2aa8d8 100644 --- a/nrgcommon/src/main/java/com/nrg948/annotations/Annotations.java +++ b/nrgcommon/src/main/java/com/nrg948/annotations/Annotations.java @@ -106,7 +106,6 @@ private static Optional loadFromMetadata() { } } catch (Exception e) { System.err.println("WARNING: Failed to load Reflections metadata: " + e.getMessage()); - reflections = null; } return reflections; diff --git a/nrgcommon/src/main/java/com/nrg948/package-info.java b/nrgcommon/src/main/java/com/nrg948/package-info.java index d768a3a..f924aba 100644 --- a/nrgcommon/src/main/java/com/nrg948/package-info.java +++ b/nrgcommon/src/main/java/com/nrg948/package-info.java @@ -27,7 +27,7 @@ of this software and associated documentation files (the "Software"), to deal * used by FIRST Robotics Competition Team 948 - Newport Robotics Group (NRG948). * *

- * To initialize the libray, you must call the {@link Common#init(String...)} + * To initialize the library, you must call the {@link Common#init(String...)} * method passing the name of the robot package. In the Command-based Robot, * this must be done in the Robot.initRobot() method before the * RobotContainer is created.
diff --git a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferences.java b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferences.java index 1f8dee7..bf4054d 100644 --- a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferences.java +++ b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferences.java @@ -30,10 +30,14 @@ of this software and associated documentation files (the "Software"), to deal import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.EnumSet; +import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.javatuples.Pair; + import com.nrg948.annotations.Annotations; import edu.wpi.first.networktables.BooleanTopic; @@ -44,8 +48,11 @@ of this software and associated documentation files (the "Software"), to deal import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.networktables.StringTopic; import edu.wpi.first.wpilibj.Preferences; +import edu.wpi.first.wpilibj.shuffleboard.BuiltInLayouts; import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets; +import edu.wpi.first.wpilibj.shuffleboard.ComplexWidget; import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; +import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardComponent; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardLayout; import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; import edu.wpi.first.wpilibj.shuffleboard.SimpleWidget; @@ -463,14 +470,28 @@ public > void visit(EnumValue value) { private static class ShuffleboardWidgetBuilder implements IValueVisitor { private ShuffleboardLayout layout; + private RobotPreferencesValue metadata; - public ShuffleboardWidgetBuilder(ShuffleboardLayout layout) { + /** + * Constructs a visitor that creates Shuffleboard widgets for preference + * values. + * + * @param layout The Shuffleboard layout to add a widget for the visited + * value. + * @param metadata A {@link RobotPreferencesValue} annotation containing the + * value's metadata. + */ + public ShuffleboardWidgetBuilder(ShuffleboardLayout layout, RobotPreferencesValue metadata) { this.layout = layout; + this.metadata = metadata; } @Override public void visit(StringValue value) { SimpleWidget widget = layout.add(value.getName(), value.getValue()).withWidget(BuiltInWidgets.kTextView); + + configureWidget(widget); + GenericEntry entry = widget.getEntry(); entry.setString(value.getValue()); @@ -488,6 +509,9 @@ public void visit(StringValue value) { public void visit(BooleanValue value) { SimpleWidget widget = layout.add(value.getName(), value.getValue()) .withWidget(BuiltInWidgets.kToggleSwitch); + + configureWidget(widget); + GenericEntry entry = widget.getEntry(); entry.setBoolean(value.getValue()); @@ -504,6 +528,9 @@ public void visit(BooleanValue value) { @Override public void visit(DoubleValue value) { SimpleWidget widget = layout.add(value.getName(), value.getValue()).withWidget(BuiltInWidgets.kTextView); + + configureWidget(widget); + GenericEntry entry = widget.getEntry(); entry.setDouble(value.getValue()); @@ -527,7 +554,9 @@ public > void visit(EnumValue value) { .forEach(e -> chooser.addOption(e.toString(), e)); chooser.setDefaultOption(currentValue.toString(), currentValue); - layout.add(value.getName(), chooser); + ComplexWidget widget = layout.add(value.getName(), chooser); + + configureWidget(widget); NetworkTableInstance ntInstance = NetworkTableInstance.getDefault(); NetworkTableEntry chooserEntry = ntInstance @@ -543,6 +572,28 @@ public > void visit(EnumValue value) { (event) -> value.setValue(event.valueData.value.getString())); } + /** + * Configures the visited value's widget according to the metadata information. + * + * @param widget The Shuffleboard widget to configure. + */ + private void configureWidget(ShuffleboardComponent widget) { + if (layout.getType().equals(BuiltInLayouts.kGrid.getLayoutName())) { + int column = metadata.column(); + int row = metadata.row(); + + if (column >= 0 && row >= 0) { + widget.withPosition(column, row); + } + + int width = metadata.width(); + int height = metadata.height(); + + if (width > 0 && height > 0) { + widget.withSize(width, height); + } + } + } } /** The name of the Shuffleboard tab containing the preferences widgets. */ @@ -581,16 +632,34 @@ public static void addShuffleBoardTab() { .asClass()); classes.stream().map(c -> c.getAnnotation(RobotPreferencesLayout.class)).forEach(layout -> { - prefsTab.getLayout(layout.groupName(), layout.type()) + var shuffleboardLayout = prefsTab.getLayout(layout.groupName(), layout.type()) .withPosition(layout.column(), layout.row()) .withSize(layout.width(), layout.height()); - }); - getAllValues().collect(Collectors.groupingBy(Value::getGroup)).forEach((group, values) -> { - ShuffleboardLayout layout = prefsTab.getLayout(group); - ShuffleboardWidgetBuilder builder = new ShuffleboardWidgetBuilder(layout); - values.stream().forEach((value) -> value.accept(builder)); + if (layout.type().equals(BuiltInLayouts.kGrid.getLayoutName())) { + int gridColumns = layout.gridColumns(); + int gridRows = layout.gridRows(); + + if (gridColumns > 0 && gridRows > 0) { + shuffleboardLayout + .withProperties(Map.of("Number of columns", gridColumns, "Number of rows", gridRows)); + } + } }); + + getFields() + .map(RobotPreferences::mapToPair) + .collect(Collectors.groupingBy(p -> p.getValue1().group)) + .forEach((group, pairs) -> { + ShuffleboardLayout layout = prefsTab.getLayout(group); + + pairs.stream() + .forEach(pair -> { + ShuffleboardWidgetBuilder builder = new ShuffleboardWidgetBuilder(layout, pair.getValue0()); + + pair.getValue1().accept(builder); + }); + }); } /** Returns a stream of fields containing preferences values. */ @@ -616,6 +685,11 @@ private static Value mapToValue(Field field) { return null; } + /** Maps a preferences field to a pair of its annotation and value instance */ + private static Pair mapToPair(Field field) { + return Pair.with(field.getAnnotation(RobotPreferencesValue.class), mapToValue(field)); + } + /** Returns all preferences values. */ private static Stream getAllValues() { return getFields().map(RobotPreferences::mapToValue).filter(v -> v != null); diff --git a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesLayout.java b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesLayout.java index c164def..b466551 100644 --- a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesLayout.java +++ b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesLayout.java @@ -82,4 +82,20 @@ of this software and associated documentation files (the "Software"), to deal * @return The layout type. */ String type() default "List Layout"; + + /** + * The number of columns in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} is "List Layout". + * + * @return The number of columns. + */ + int gridColumns() default -1; + + /** + * The number of rows in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} is "List Layout". + * + * @return The number of rows. + */ + int gridRows() default -1; } diff --git a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesValue.java b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesValue.java index 1c5b057..dd01c5d 100644 --- a/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesValue.java +++ b/nrgcommon/src/main/java/com/nrg948/preferences/RobotPreferencesValue.java @@ -33,5 +33,35 @@ of this software and associated documentation files (the "Software"), to deal @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface RobotPreferencesValue { + /** + * The column position in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} of the value's group is "List Layout". + * + * @return The column position of this value. + */ + int column() default -1; + /** + * The row position in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} of the value's group is "List Layout". + * + * @return The row position of this value. + */ + int row() default -1; + + /** + * The width of this value in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} of the value's group is "List Layout". + * + * @return The width of this value. + */ + int width() default -1; + + /** + * The height of this value in a grid layout. This value is ignored if the + * {@link RobotPreferencesLayout#type} of the value's group is "List Layout". + * + * @return The height of this value. + */ + int height() default -1; } \ No newline at end of file diff --git a/nrgcommon/src/main/java/com/nrg948/preferences/package-info.java b/nrgcommon/src/main/java/com/nrg948/preferences/package-info.java index d482791..c01ad40 100644 --- a/nrgcommon/src/main/java/com/nrg948/preferences/package-info.java +++ b/nrgcommon/src/main/java/com/nrg948/preferences/package-info.java @@ -33,7 +33,7 @@ of this software and associated documentation files (the "Software"), to deal * group of PID preferences to be added automatically to Shuffleboard. * *

- * The followng example defines the PID constants in a class derived from + * The following example defines the PID constants in a class derived from * {@link PIDSubsystem} and enables a layout to be generated for the * Shuffleboard.
*