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 protected ShuffleboardComponent(ShuffleboardContainer parent, String title, String type) { 030 m_parent = requireNonNullParam(parent, "parent", "ShuffleboardComponent"); 031 m_title = requireNonNullParam(title, "title", "ShuffleboardComponent"); 032 m_type = type; 033 } 034 035 protected ShuffleboardComponent(ShuffleboardContainer parent, String title) { 036 this(parent, title, null); 037 } 038 039 public final ShuffleboardContainer getParent() { 040 return m_parent; 041 } 042 043 protected final void setType(String type) { 044 m_type = type; 045 m_metadataDirty = true; 046 } 047 048 public final String getType() { 049 return m_type; 050 } 051 052 @Override 053 public final String getTitle() { 054 return m_title; 055 } 056 057 /** Gets the custom properties for this component. May be null. */ 058 final Map<String, Object> getProperties() { 059 return m_properties; 060 } 061 062 /** 063 * Sets custom properties for this component. Property names are case- and whitespace-insensitive 064 * (capitalization and spaces do not matter). 065 * 066 * @param properties the properties for this component 067 * @return this component 068 */ 069 @SuppressWarnings("unchecked") 070 public final C withProperties(Map<String, Object> properties) { 071 m_properties = properties; 072 m_metadataDirty = true; 073 return (C) this; 074 } 075 076 /** 077 * Sets the position of this component in the tab. This has no effect if this component is inside 078 * a layout. 079 * 080 * <p>If the position of a single component is set, it is recommended to set the positions of 081 * <i>all</i> components inside a tab to prevent Shuffleboard from automatically placing another 082 * component there before the one with the specific position is sent. 083 * 084 * @param columnIndex the column in the tab to place this component 085 * @param rowIndex the row in the tab to place this component 086 * @return this component 087 */ 088 @SuppressWarnings("unchecked") 089 public final C withPosition(int columnIndex, int rowIndex) { 090 m_column = columnIndex; 091 m_row = rowIndex; 092 m_metadataDirty = true; 093 return (C) this; 094 } 095 096 /** 097 * Sets the size of this component in the tab. This has no effect if this component is inside a 098 * layout. 099 * 100 * @param width how many columns wide the component should be 101 * @param height how many rows high the component should be 102 * @return this component 103 */ 104 @SuppressWarnings("unchecked") 105 public final C withSize(int width, int height) { 106 m_width = width; 107 m_height = height; 108 m_metadataDirty = true; 109 return (C) this; 110 } 111 112 protected final void buildMetadata(NetworkTable metaTable) { 113 if (!m_metadataDirty) { 114 return; 115 } 116 // Component type 117 if (getType() == null) { 118 metaTable.getEntry("PreferredComponent").unpublish(); 119 } else { 120 metaTable.getEntry("PreferredComponent").setString(getType()); 121 } 122 123 // Tile size 124 if (m_width <= 0 || m_height <= 0) { 125 metaTable.getEntry("Size").unpublish(); 126 } else { 127 metaTable.getEntry("Size").setDoubleArray(new double[] {m_width, m_height}); 128 } 129 130 // Tile position 131 if (m_column < 0 || m_row < 0) { 132 metaTable.getEntry("Position").unpublish(); 133 } else { 134 metaTable.getEntry("Position").setDoubleArray(new double[] {m_column, m_row}); 135 } 136 137 // Custom properties 138 if (getProperties() != null) { 139 NetworkTable propTable = metaTable.getSubTable("Properties"); 140 getProperties().forEach((name, value) -> propTable.getEntry(name).setValue(value)); 141 } 142 m_metadataDirty = false; 143 } 144}