001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj.shuffleboard; 006 007import static edu.wpi.first.util.ErrorMessages.requireNonNullParam; 008 009import edu.wpi.first.networktables.NetworkTable; 010import java.util.Map; 011 012/** 013 * A generic component in Shuffleboard. 014 * 015 * @param <C> the self type 016 */ 017public abstract class ShuffleboardComponent<C extends ShuffleboardComponent<C>> 018 implements ShuffleboardValue { 019 private final ShuffleboardContainer m_parent; 020 private final String m_title; 021 private String m_type; 022 private Map<String, Object> m_properties; 023 private boolean m_metadataDirty = true; 024 private int m_column = -1; 025 private int m_row = -1; 026 private int m_width = -1; 027 private int m_height = -1; 028 029 /** 030 * Constructs a ShuffleboardComponent. 031 * 032 * @param parent The parent container. 033 * @param title The component title. 034 * @param type The component type. 035 */ 036 protected ShuffleboardComponent(ShuffleboardContainer parent, String title, String type) { 037 m_parent = requireNonNullParam(parent, "parent", "ShuffleboardComponent"); 038 m_title = requireNonNullParam(title, "title", "ShuffleboardComponent"); 039 m_type = type; 040 } 041 042 /** 043 * Constructs a ShuffleboardComponent. 044 * 045 * @param parent The parent container. 046 * @param title The component title. 047 */ 048 protected ShuffleboardComponent(ShuffleboardContainer parent, String title) { 049 this(parent, title, null); 050 } 051 052 /** 053 * Returns the parent container. 054 * 055 * @return The parent container. 056 */ 057 public final ShuffleboardContainer getParent() { 058 return m_parent; 059 } 060 061 /** 062 * Sets the component type. 063 * 064 * @param type The component type. 065 */ 066 protected final void setType(String type) { 067 m_type = type; 068 m_metadataDirty = true; 069 } 070 071 /** 072 * Returns the component type. 073 * 074 * @return The component type. 075 */ 076 public final String getType() { 077 return m_type; 078 } 079 080 @Override 081 public final String getTitle() { 082 return m_title; 083 } 084 085 /** Gets the custom properties for this component. May be null. */ 086 final Map<String, Object> getProperties() { 087 return m_properties; 088 } 089 090 /** 091 * Sets custom properties for this component. Property names are case- and whitespace-insensitive 092 * (capitalization and spaces do not matter). 093 * 094 * @param properties the properties for this component 095 * @return this component 096 */ 097 @SuppressWarnings("unchecked") 098 public final C withProperties(Map<String, Object> properties) { 099 m_properties = properties; 100 m_metadataDirty = true; 101 return (C) this; 102 } 103 104 /** 105 * Sets the position of this component in the tab. This has no effect if this component is inside 106 * a layout. 107 * 108 * <p>If the position of a single component is set, it is recommended to set the positions of 109 * <i>all</i> components inside a tab to prevent Shuffleboard from automatically placing another 110 * component there before the one with the specific position is sent. 111 * 112 * @param columnIndex the column in the tab to place this component 113 * @param rowIndex the row in the tab to place this component 114 * @return this component 115 */ 116 @SuppressWarnings("unchecked") 117 public final C withPosition(int columnIndex, int rowIndex) { 118 m_column = columnIndex; 119 m_row = rowIndex; 120 m_metadataDirty = true; 121 return (C) this; 122 } 123 124 /** 125 * Sets the size of this component in the tab. This has no effect if this component is inside a 126 * layout. 127 * 128 * @param width how many columns wide the component should be 129 * @param height how many rows high the component should be 130 * @return this component 131 */ 132 @SuppressWarnings("unchecked") 133 public final C withSize(int width, int height) { 134 m_width = width; 135 m_height = height; 136 m_metadataDirty = true; 137 return (C) this; 138 } 139 140 /** 141 * Builds NT metadata. 142 * 143 * @param metaTable The NT metadata table. 144 */ 145 protected final void buildMetadata(NetworkTable metaTable) { 146 if (!m_metadataDirty) { 147 return; 148 } 149 // Component type 150 if (getType() == null) { 151 metaTable.getEntry("PreferredComponent").unpublish(); 152 } else { 153 metaTable.getEntry("PreferredComponent").setString(getType()); 154 } 155 156 // Tile size 157 if (m_width <= 0 || m_height <= 0) { 158 metaTable.getEntry("Size").unpublish(); 159 } else { 160 metaTable.getEntry("Size").setDoubleArray(new double[] {m_width, m_height}); 161 } 162 163 // Tile position 164 if (m_column < 0 || m_row < 0) { 165 metaTable.getEntry("Position").unpublish(); 166 } else { 167 metaTable.getEntry("Position").setDoubleArray(new double[] {m_column, m_row}); 168 } 169 170 // Custom properties 171 if (getProperties() != null) { 172 NetworkTable propTable = metaTable.getSubTable("Properties"); 173 getProperties().forEach((name, value) -> propTable.getEntry(name).setValue(value)); 174 } 175 m_metadataDirty = false; 176 } 177}