From 341ab81b796f42e54940d353ddbcf5e0e62afea7 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 24 Nov 2023 20:47:05 +0100 Subject: [PATCH] [#2338] Use searchable and categorizable combobox for flight data type --- core/resources/l10n/messages.properties | 17 + .../openrocket/simulation/FlightDataType.java | 192 +++++---- .../simulation/FlightDataTypeGroup.java | 28 +- .../gui/simulation/FlightDataComboBox.java | 407 +++++++++++++++--- .../gui/simulation/SimulationPlotPanel.java | 3 +- .../gui/widgets/PlaceholderTextField.java | 55 +++ 6 files changed, 569 insertions(+), 133 deletions(-) create mode 100644 swing/src/net/sf/openrocket/gui/widgets/PlaceholderTextField.java diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 96b47e1f4..47c633608 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -811,6 +811,9 @@ simplotpanel.MarkerStyle.btn.VerticalMarker = Vertical line simplotpanel.MarkerStyle.btn.Icon = Icon simplotpanel.MarkerStyle.OnlyInTime = Only available for time domain, other domains only support icon markers +! FlightDataComboBox +FlightDataComboBox.placeholder = Enter the data type + ! Component add buttons compaddbuttons.AxialStage = Stage compaddbuttons.Bodycompandfinsets = Body Components and Fin Sets @@ -2062,6 +2065,20 @@ FlightDataType.TYPE_LONGITUDE = Longitude FlightDataType.TYPE_CORIOLIS_ACCELERATION = Coriolis acceleration FlightDataType.TYPE_GRAVITY = Gravitational acceleration +! FlightDataTypeGroup +FlightDataTypeGroup.GROUP_TIME = Time +FlightDataTypeGroup.GROUP_POSITION_AND_MOTION = Position and Motion +FlightDataTypeGroup.GROUP_ORIENTATION = Orientation +FlightDataTypeGroup.GROUP_MASS_AND_INERTIA = Mass and Inertia +FlightDataTypeGroup.GROUP_STABILITY = Stability +FlightDataTypeGroup.GROUP_THRUST_AND_DRAG = Thrust and Drag +FlightDataTypeGroup.GROUP_COEFFICIENTS = Coefficients +FlightDataTypeGroup.GROUP_ATMOSPHERIC_CONDITIONS = Atmospheric Conditions +FlightDataTypeGroup.GROUP_CHARACTERISTIC_NUMBERS = Characteristic Numbers +FlightDataTypeGroup.GROUP_REFERENCE_VALUES = Reference Values +FlightDataTypeGroup.GROUP_SIMULATION_INFORMATION = Simulation Information +FlightDataTypeGroup.GROUP_CUSTOM = Custom + ! PlotConfiguration PlotConfiguration.Verticalmotion = Vertical motion vs. time PlotConfiguration.Totalmotion = Total motion vs. time diff --git a/core/src/net/sf/openrocket/simulation/FlightDataType.java b/core/src/net/sf/openrocket/simulation/FlightDataType.java index 88856370d..679f70371 100644 --- a/core/src/net/sf/openrocket/simulation/FlightDataType.java +++ b/core/src/net/sf/openrocket/simulation/FlightDataType.java @@ -38,23 +38,21 @@ public class FlightDataType implements Comparable { //// Time - public static final FlightDataType TYPE_TIME = newType(trans.get("FlightDataType.TYPE_TIME"), "t", UnitGroup.UNITS_FLIGHT_TIME, 1); + public static final FlightDataType TYPE_TIME = newType(trans.get("FlightDataType.TYPE_TIME"), "t", UnitGroup.UNITS_FLIGHT_TIME, + FlightDataTypeGroup.TIME, 0); - //// Vertical position and motion + //// Position and motion //// Altitude public static final FlightDataType TYPE_ALTITUDE = newType(trans.get("FlightDataType.TYPE_ALTITUDE"), "h", UnitGroup.UNITS_DISTANCE, FlightDataTypeGroup.POSITION_AND_MOTION, 0); //// Vertical velocity public static final FlightDataType TYPE_VELOCITY_Z = newType(trans.get("FlightDataType.TYPE_VELOCITY_Z"), "Vz", UnitGroup.UNITS_VELOCITY, FlightDataTypeGroup.POSITION_AND_MOTION, 1); - //// Vertical acceleration - public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION, - FlightDataTypeGroup.POSITION_AND_MOTION, 2); - - - //// Total motion //// Total velocity public static final FlightDataType TYPE_VELOCITY_TOTAL = newType(trans.get("FlightDataType.TYPE_VELOCITY_TOTAL"), "Vt", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.POSITION_AND_MOTION, 2); + //// Vertical acceleration + public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION, FlightDataTypeGroup.POSITION_AND_MOTION, 3); //// Total acceleration public static final FlightDataType TYPE_ACCELERATION_TOTAL = newType(trans.get("FlightDataType.TYPE_ACCELERATION_TOTAL"), "At", UnitGroup.UNITS_ACCELERATION, @@ -64,143 +62,179 @@ public class FlightDataType implements Comparable { //// Lateral position and motion //// Position East of launch public static final FlightDataType TYPE_POSITION_X = newType(trans.get("FlightDataType.TYPE_POSITION_X"), "Px", UnitGroup.UNITS_DISTANCE, - FlightDataTypeGroup.POSITION_AND_MOTION, 5); + FlightDataTypeGroup.POSITION_AND_MOTION, 0); //// Position North of launch public static final FlightDataType TYPE_POSITION_Y = newType(trans.get("FlightDataType.TYPE_POSITION_Y"), "Py", UnitGroup.UNITS_DISTANCE, - FlightDataTypeGroup.POSITION_AND_MOTION, 6); + FlightDataTypeGroup.POSITION_AND_MOTION, 1); //// Lateral distance public static final FlightDataType TYPE_POSITION_XY = newType(trans.get("FlightDataType.TYPE_POSITION_XY"), "Pl", UnitGroup.UNITS_DISTANCE, - FlightDataTypeGroup.POSITION_AND_MOTION, 7); + FlightDataTypeGroup.POSITION_AND_MOTION, 2); //// Lateral direction public static final FlightDataType TYPE_POSITION_DIRECTION = newType(trans.get("FlightDataType.TYPE_POSITION_DIRECTION"), "\u03b8l", UnitGroup.UNITS_ANGLE, - FlightDataTypeGroup.POSITION_AND_MOTION, 8); + FlightDataTypeGroup.POSITION_AND_MOTION, 3); //// Lateral velocity public static final FlightDataType TYPE_VELOCITY_XY = newType(trans.get("FlightDataType.TYPE_VELOCITY_XY"), "Vl", UnitGroup.UNITS_VELOCITY, - FlightDataTypeGroup.POSITION_AND_MOTION, 9); + FlightDataTypeGroup.POSITION_AND_MOTION, 4); //// Lateral acceleration public static final FlightDataType TYPE_ACCELERATION_XY = newType(trans.get("FlightDataType.TYPE_ACCELERATION_XY"), "Al", UnitGroup.UNITS_ACCELERATION, - FlightDataTypeGroup.POSITION_AND_MOTION, 10); + FlightDataTypeGroup.POSITION_AND_MOTION, 5); //// Latitude public static final FlightDataType TYPE_LATITUDE = newType(trans.get("FlightDataType.TYPE_LATITUDE"), "\u03c6", UnitGroup.UNITS_ANGLE, - FlightDataTypeGroup.POSITION_AND_MOTION, 11); + FlightDataTypeGroup.POSITION_AND_MOTION, 6); //// Longitude public static final FlightDataType TYPE_LONGITUDE = newType(trans.get("FlightDataType.TYPE_LONGITUDE"), "\u03bb", UnitGroup.UNITS_ANGLE, - FlightDataTypeGroup.POSITION_AND_MOTION, 12); - - //// Gravity - public static final FlightDataType TYPE_GRAVITY = newType(trans.get("FlightDataType.TYPE_GRAVITY"), "g", UnitGroup.UNITS_ACCELERATION, 38); - - //// Angular motion + FlightDataTypeGroup.POSITION_AND_MOTION, 7); + + + // Orientation //// Angle of attack - public static final FlightDataType TYPE_AOA = newType(trans.get("FlightDataType.TYPE_AOA"), "\u03b1", UnitGroup.UNITS_ANGLE, 40); + public static final FlightDataType TYPE_AOA = newType(trans.get("FlightDataType.TYPE_AOA"), "\u03b1", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.ORIENTATION, 0); //// Roll rate - public static final FlightDataType TYPE_ROLL_RATE = newType(trans.get("FlightDataType.TYPE_ROLL_RATE"), "d\u03a6", UnitGroup.UNITS_ROLL, 41); + public static final FlightDataType TYPE_ROLL_RATE = newType(trans.get("FlightDataType.TYPE_ROLL_RATE"), "d\u03a6", UnitGroup.UNITS_ROLL, + FlightDataTypeGroup.ORIENTATION, 1); //// Pitch rate - public static final FlightDataType TYPE_PITCH_RATE = newType(trans.get("FlightDataType.TYPE_PITCH_RATE"), "d\u03b8", UnitGroup.UNITS_ROLL, 42); + public static final FlightDataType TYPE_PITCH_RATE = newType(trans.get("FlightDataType.TYPE_PITCH_RATE"), "d\u03b8", UnitGroup.UNITS_ROLL, + FlightDataTypeGroup.ORIENTATION, 2); //// Yaw rate - public static final FlightDataType TYPE_YAW_RATE = newType(trans.get("FlightDataType.TYPE_YAW_RATE"), "d\u03a8", UnitGroup.UNITS_ROLL, 43); + public static final FlightDataType TYPE_YAW_RATE = newType(trans.get("FlightDataType.TYPE_YAW_RATE"), "d\u03a8", UnitGroup.UNITS_ROLL, + FlightDataTypeGroup.ORIENTATION, 3); + //// Vertical orientation (zenith) + public static final FlightDataType TYPE_ORIENTATION_THETA = newType(trans.get("FlightDataType.TYPE_ORIENTATION_THETA"), "\u0398", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.ORIENTATION, 4); + //// Lateral orientation (azimuth) + public static final FlightDataType TYPE_ORIENTATION_PHI = newType(trans.get("FlightDataType.TYPE_ORIENTATION_PHI"), "\u03a6", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.ORIENTATION, 5); + - - //// Stability information + // Mass and inertia //// Mass - public static final FlightDataType TYPE_MASS = newType(trans.get("FlightDataType.TYPE_MASS"), "m", UnitGroup.UNITS_MASS, 50); + public static final FlightDataType TYPE_MASS = newType(trans.get("FlightDataType.TYPE_MASS"), "m", UnitGroup.UNITS_MASS, + FlightDataTypeGroup.MASS_AND_INERTIA, 0); //// Motor mass - public static final FlightDataType TYPE_MOTOR_MASS = newType(trans.get("FlightDataType.TYPE_MOTOR_MASS"), "mp", UnitGroup.UNITS_MASS, 51); + public static final FlightDataType TYPE_MOTOR_MASS = newType(trans.get("FlightDataType.TYPE_MOTOR_MASS"), "mp", UnitGroup.UNITS_MASS, + FlightDataTypeGroup.MASS_AND_INERTIA, 1); //// Longitudinal moment of inertia - public static final FlightDataType TYPE_LONGITUDINAL_INERTIA = newType(trans.get("FlightDataType.TYPE_LONGITUDINAL_INERTIA"), "Il", UnitGroup.UNITS_INERTIA, 52); + public static final FlightDataType TYPE_LONGITUDINAL_INERTIA = newType(trans.get("FlightDataType.TYPE_LONGITUDINAL_INERTIA"), "Il", UnitGroup.UNITS_INERTIA, + FlightDataTypeGroup.MASS_AND_INERTIA, 2); //// Rotational moment of inertia - public static final FlightDataType TYPE_ROTATIONAL_INERTIA = newType(trans.get("FlightDataType.TYPE_ROTATIONAL_INERTIA"), "Ir", UnitGroup.UNITS_INERTIA, 53); + public static final FlightDataType TYPE_ROTATIONAL_INERTIA = newType(trans.get("FlightDataType.TYPE_ROTATIONAL_INERTIA"), "Ir", UnitGroup.UNITS_INERTIA, + FlightDataTypeGroup.MASS_AND_INERTIA, 3); + //// Gravity + public static final FlightDataType TYPE_GRAVITY = newType(trans.get("FlightDataType.TYPE_GRAVITY"), "g", UnitGroup.UNITS_ACCELERATION, + FlightDataTypeGroup.MASS_AND_INERTIA, 4); + + // Stability //// CP location - public static final FlightDataType TYPE_CP_LOCATION = newType(trans.get("FlightDataType.TYPE_CP_LOCATION"), "Cp", UnitGroup.UNITS_LENGTH, 54); + public static final FlightDataType TYPE_CP_LOCATION = newType(trans.get("FlightDataType.TYPE_CP_LOCATION"), "Cp", UnitGroup.UNITS_LENGTH, + FlightDataTypeGroup.STABILITY, 0); //// CG location - public static final FlightDataType TYPE_CG_LOCATION = newType(trans.get("FlightDataType.TYPE_CG_LOCATION"), "Cg", UnitGroup.UNITS_LENGTH, 55); + public static final FlightDataType TYPE_CG_LOCATION = newType(trans.get("FlightDataType.TYPE_CG_LOCATION"), "Cg", UnitGroup.UNITS_LENGTH, + FlightDataTypeGroup.STABILITY, 1); //// Stability margin calibers - public static final FlightDataType TYPE_STABILITY = newType(trans.get("FlightDataType.TYPE_STABILITY"), "S", UnitGroup.UNITS_COEFFICIENT, 56); + public static final FlightDataType TYPE_STABILITY = newType(trans.get("FlightDataType.TYPE_STABILITY"), "S", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.STABILITY, 2); - //// Characteristic numbers + // Characteristic numbers //// Mach number - public static final FlightDataType TYPE_MACH_NUMBER = newType(trans.get("FlightDataType.TYPE_MACH_NUMBER"), "M", UnitGroup.UNITS_COEFFICIENT, 60); + public static final FlightDataType TYPE_MACH_NUMBER = newType(trans.get("FlightDataType.TYPE_MACH_NUMBER"), "M", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.CHARACTERISTIC_NUMBERS, 0); //// Reynolds number - public static final FlightDataType TYPE_REYNOLDS_NUMBER = newType(trans.get("FlightDataType.TYPE_REYNOLDS_NUMBER"), "R", UnitGroup.UNITS_COEFFICIENT, 61); + public static final FlightDataType TYPE_REYNOLDS_NUMBER = newType(trans.get("FlightDataType.TYPE_REYNOLDS_NUMBER"), "R", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.CHARACTERISTIC_NUMBERS, 1); - //// Thrust and drag + // Thrust and drag //// Thrust - public static final FlightDataType TYPE_THRUST_FORCE = newType(trans.get("FlightDataType.TYPE_THRUST_FORCE"), "Ft", UnitGroup.UNITS_FORCE, 70); + public static final FlightDataType TYPE_THRUST_FORCE = newType(trans.get("FlightDataType.TYPE_THRUST_FORCE"), "Ft", UnitGroup.UNITS_FORCE, + FlightDataTypeGroup.THRUST_AND_DRAG, 0); //// Thrust-to-weight ratio - public static final FlightDataType TYPE_THRUST_WEIGHT_RATIO = newType(trans.get("FlightDataType.TYPE_THRUST_WEIGHT_RATIO"), "Twr", UnitGroup.UNITS_COEFFICIENT, 71); + public static final FlightDataType TYPE_THRUST_WEIGHT_RATIO = newType(trans.get("FlightDataType.TYPE_THRUST_WEIGHT_RATIO"), "Twr", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 1); //// Drag force - public static final FlightDataType TYPE_DRAG_FORCE = newType(trans.get("FlightDataType.TYPE_DRAG_FORCE"), "Fd", UnitGroup.UNITS_FORCE, 72); + public static final FlightDataType TYPE_DRAG_FORCE = newType(trans.get("FlightDataType.TYPE_DRAG_FORCE"), "Fd", UnitGroup.UNITS_FORCE, + FlightDataTypeGroup.THRUST_AND_DRAG, 2); //// Drag coefficient - public static final FlightDataType TYPE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_DRAG_COEFF"), "Cd", UnitGroup.UNITS_COEFFICIENT, 73); - //// Axial drag coefficient - public static final FlightDataType TYPE_AXIAL_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_AXIAL_DRAG_COEFF"), "Cda", UnitGroup.UNITS_COEFFICIENT, 74); - - - //// Component drag coefficients + public static final FlightDataType TYPE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_DRAG_COEFF"), "Cd", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 3); //// Friction drag coefficient - public static final FlightDataType TYPE_FRICTION_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_FRICTION_DRAG_COEFF"), "Cdf", UnitGroup.UNITS_COEFFICIENT, 80); + public static final FlightDataType TYPE_FRICTION_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_FRICTION_DRAG_COEFF"), "Cdf", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 4); //// Pressure drag coefficient - public static final FlightDataType TYPE_PRESSURE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_PRESSURE_DRAG_COEFF"), "Cdp", UnitGroup.UNITS_COEFFICIENT, 81); + public static final FlightDataType TYPE_PRESSURE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_PRESSURE_DRAG_COEFF"), "Cdp", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 5); //// Base drag coefficient - public static final FlightDataType TYPE_BASE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_BASE_DRAG_COEFF"), "Cdb", UnitGroup.UNITS_COEFFICIENT, 82); + public static final FlightDataType TYPE_BASE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_BASE_DRAG_COEFF"), "Cdb", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 6); + //// Axial drag coefficient + public static final FlightDataType TYPE_AXIAL_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_AXIAL_DRAG_COEFF"), "Cda", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.THRUST_AND_DRAG, 7); - //// Other coefficients + // Coefficients //// Normal force coefficient - public static final FlightDataType TYPE_NORMAL_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_NORMAL_FORCE_COEFF"), "Cn", UnitGroup.UNITS_COEFFICIENT, 90); + public static final FlightDataType TYPE_NORMAL_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_NORMAL_FORCE_COEFF"), "Cn", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 0); //// Pitch moment coefficient - public static final FlightDataType TYPE_PITCH_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_MOMENT_COEFF"), "C\u03b8", UnitGroup.UNITS_COEFFICIENT, 91); + public static final FlightDataType TYPE_PITCH_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_MOMENT_COEFF"), "C\u03b8", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 1); //// Yaw moment coefficient - public static final FlightDataType TYPE_YAW_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_MOMENT_COEFF"), "C\u03c4\u03a8", UnitGroup.UNITS_COEFFICIENT, 92); + public static final FlightDataType TYPE_YAW_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_MOMENT_COEFF"), "C\u03c4\u03a8", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 2); //// Side force coefficient - public static final FlightDataType TYPE_SIDE_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_SIDE_FORCE_COEFF"), "C\u03c4s", UnitGroup.UNITS_COEFFICIENT, 93); + public static final FlightDataType TYPE_SIDE_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_SIDE_FORCE_COEFF"), "C\u03c4s", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 3); //// Roll moment coefficient - public static final FlightDataType TYPE_ROLL_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_MOMENT_COEFF"), "C\u03c4\u03a6", UnitGroup.UNITS_COEFFICIENT, 94); + public static final FlightDataType TYPE_ROLL_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_MOMENT_COEFF"), "C\u03c4\u03a6", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 4); //// Roll forcing coefficient - public static final FlightDataType TYPE_ROLL_FORCING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_FORCING_COEFF"), "Cf\u03a6", UnitGroup.UNITS_COEFFICIENT, 95); + public static final FlightDataType TYPE_ROLL_FORCING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_FORCING_COEFF"), "Cf\u03a6", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 5); //// Roll damping coefficient - public static final FlightDataType TYPE_ROLL_DAMPING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_DAMPING_COEFF"), "C\u03b6\u03a6", UnitGroup.UNITS_COEFFICIENT, 96); - + public static final FlightDataType TYPE_ROLL_DAMPING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_DAMPING_COEFF"), "C\u03b6\u03a6", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 6); //// Pitch damping coefficient - public static final FlightDataType TYPE_PITCH_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF"), "C\u03b6\u03b8", UnitGroup.UNITS_COEFFICIENT, 97); + public static final FlightDataType TYPE_PITCH_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF"), "C\u03b6\u03b8", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 7); //// Yaw damping coefficient - public static final FlightDataType TYPE_YAW_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF"), "C\u03b6\u03a8", UnitGroup.UNITS_COEFFICIENT, 98); + public static final FlightDataType TYPE_YAW_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF"), "C\u03b6\u03a8", UnitGroup.UNITS_COEFFICIENT, + FlightDataTypeGroup.COEFFICIENTS, 8); //// Coriolis acceleration public static final FlightDataType TYPE_CORIOLIS_ACCELERATION = newType(trans.get("FlightDataType.TYPE_CORIOLIS_ACCELERATION"), "Ac", UnitGroup.UNITS_ACCELERATION, 99); - //// Reference length + area + // Reference values //// Reference length - public static final FlightDataType TYPE_REFERENCE_LENGTH = newType(trans.get("FlightDataType.TYPE_REFERENCE_LENGTH"), "Lr", UnitGroup.UNITS_LENGTH, 100); + public static final FlightDataType TYPE_REFERENCE_LENGTH = newType(trans.get("FlightDataType.TYPE_REFERENCE_LENGTH"), "Lr", UnitGroup.UNITS_LENGTH, + FlightDataTypeGroup.REFERENCE_VALUES, 0); //// Reference area - public static final FlightDataType TYPE_REFERENCE_AREA = newType(trans.get("FlightDataType.TYPE_REFERENCE_AREA"), "Ar", UnitGroup.UNITS_AREA, 101); + public static final FlightDataType TYPE_REFERENCE_AREA = newType(trans.get("FlightDataType.TYPE_REFERENCE_AREA"), "Ar", UnitGroup.UNITS_AREA, + FlightDataTypeGroup.REFERENCE_VALUES, 1); - //// Orientation - //// Vertical orientation (zenith) - public static final FlightDataType TYPE_ORIENTATION_THETA = newType(trans.get("FlightDataType.TYPE_ORIENTATION_THETA"), "\u0398", UnitGroup.UNITS_ANGLE, 106); - //// Lateral orientation (azimuth) - public static final FlightDataType TYPE_ORIENTATION_PHI = newType(trans.get("FlightDataType.TYPE_ORIENTATION_PHI"), "\u03a6", UnitGroup.UNITS_ANGLE, 107); - - - //// Atmospheric conditions + // Atmospheric conditions //// Wind velocity - public static final FlightDataType TYPE_WIND_VELOCITY = newType(trans.get("FlightDataType.TYPE_WIND_VELOCITY"), "Vw", UnitGroup.UNITS_VELOCITY, 110); + public static final FlightDataType TYPE_WIND_VELOCITY = newType(trans.get("FlightDataType.TYPE_WIND_VELOCITY"), "Vw", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 0); //// Air temperature - public static final FlightDataType TYPE_AIR_TEMPERATURE = newType(trans.get("FlightDataType.TYPE_AIR_TEMPERATURE"), "T", UnitGroup.UNITS_TEMPERATURE, 111); + public static final FlightDataType TYPE_AIR_TEMPERATURE = newType(trans.get("FlightDataType.TYPE_AIR_TEMPERATURE"), "T", UnitGroup.UNITS_TEMPERATURE, + FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 1); //// Air pressure - public static final FlightDataType TYPE_AIR_PRESSURE = newType(trans.get("FlightDataType.TYPE_AIR_PRESSURE"), "P", UnitGroup.UNITS_PRESSURE, 112); + public static final FlightDataType TYPE_AIR_PRESSURE = newType(trans.get("FlightDataType.TYPE_AIR_PRESSURE"), "P", UnitGroup.UNITS_PRESSURE, + FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 2); //// Speed of sound - public static final FlightDataType TYPE_SPEED_OF_SOUND = newType(trans.get("FlightDataType.TYPE_SPEED_OF_SOUND"), "Vs", UnitGroup.UNITS_VELOCITY, 113); + public static final FlightDataType TYPE_SPEED_OF_SOUND = newType(trans.get("FlightDataType.TYPE_SPEED_OF_SOUND"), "Vs", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 3); - //// Simulation information + // Simulation information //// Simulation time step - public static final FlightDataType TYPE_TIME_STEP = newType(trans.get("FlightDataType.TYPE_TIME_STEP"), "dt", UnitGroup.UNITS_TIME_STEP, 200); + public static final FlightDataType TYPE_TIME_STEP = newType(trans.get("FlightDataType.TYPE_TIME_STEP"), "dt", UnitGroup.UNITS_TIME_STEP, + FlightDataTypeGroup.SIMULATION_INFORMATION, 0); //// Computation time - public static final FlightDataType TYPE_COMPUTATION_TIME = newType(trans.get("FlightDataType.TYPE_COMPUTATION_TIME"), "tc", UnitGroup.UNITS_SHORT_TIME, 201); + public static final FlightDataType TYPE_COMPUTATION_TIME = newType(trans.get("FlightDataType.TYPE_COMPUTATION_TIME"), "tc", UnitGroup.UNITS_SHORT_TIME, + FlightDataTypeGroup.SIMULATION_INFORMATION, 1); // An array of all the built in types public static final FlightDataType[] ALL_TYPES = { diff --git a/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java b/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java index 296374031..0b4b837cb 100644 --- a/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java +++ b/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java @@ -6,14 +6,34 @@ import net.sf.openrocket.startup.Application; public class FlightDataTypeGroup { private static final Translator trans = Application.getTranslator(); - public static final FlightDataTypeGroup POSITION_AND_MOTION = new FlightDataTypeGroup("Position and motion", 0); + public static final FlightDataTypeGroup TIME = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_TIME"), 0); + public static final FlightDataTypeGroup POSITION_AND_MOTION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_POSITION_AND_MOTION"), 10); + public static final FlightDataTypeGroup ORIENTATION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_ORIENTATION"), 20); + public static final FlightDataTypeGroup MASS_AND_INERTIA = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_MASS_AND_INERTIA"), 30); + public static final FlightDataTypeGroup STABILITY = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_STABILITY"), 40); + public static final FlightDataTypeGroup THRUST_AND_DRAG = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_THRUST_AND_DRAG"), 50); + public static final FlightDataTypeGroup COEFFICIENTS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_COEFFICIENTS"), 60); + public static final FlightDataTypeGroup ATMOSPHERIC_CONDITIONS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_ATMOSPHERIC_CONDITIONS"), 70); + public static final FlightDataTypeGroup CHARACTERISTIC_NUMBERS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_CHARACTERISTIC_NUMBERS"), 80); + public static final FlightDataTypeGroup REFERENCE_VALUES = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_REFERENCE_VALUES"), 90); + public static final FlightDataTypeGroup SIMULATION_INFORMATION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_SIMULATION_INFORMATION"), 100); - public static final FlightDataTypeGroup CUSTOM = new FlightDataTypeGroup("Custom", 100); + public static final FlightDataTypeGroup CUSTOM = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_CUSTOM"), 200); // An array of all the built-in groups public static final FlightDataTypeGroup[] ALL_GROUPS = { - POSITION_AND_MOTION, - CUSTOM + TIME, + POSITION_AND_MOTION, + ORIENTATION, + MASS_AND_INERTIA, + STABILITY, + THRUST_AND_DRAG, + COEFFICIENTS, + ATMOSPHERIC_CONDITIONS, + CHARACTERISTIC_NUMBERS, + REFERENCE_VALUES, + SIMULATION_INFORMATION, + CUSTOM }; private final String name; diff --git a/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java b/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java index 80931952e..118c13d8d 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java +++ b/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java @@ -1,14 +1,33 @@ package net.sf.openrocket.gui.simulation; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.UITheme; +import net.sf.openrocket.gui.widgets.PlaceholderTextField; +import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightDataTypeGroup; +import net.sf.openrocket.startup.Application; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.plaf.basic.BasicArrowButton; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Point; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -16,49 +35,268 @@ import java.util.ArrayList; import java.util.Hashtable; public class FlightDataComboBox extends JComboBox { - private final JPopupMenu mainMenu; - private final Hashtable subItems = new Hashtable<>(); + private static final Translator trans = Application.getTranslator(); - public FlightDataComboBox(FlightDataType[] types) { + private final JPopupMenu categoryPopup; + private final JPopupMenu searchPopup; + private final PlaceholderTextField searchFieldCategory; + private final PlaceholderTextField searchFieldSearch; + private final JList filteredList; + + private final FlightDataType[] allTypes; + private final Hashtable typeGroupMap; + + private int highlightedListIdx = -1; + + private static Color textSelectionBackground; + + static { + initColors(); + } + + public FlightDataComboBox(FlightDataTypeGroup[] allGroups, FlightDataType[] types) { super(types); setEditable(false); - for (FlightDataTypeGroup group : FlightDataTypeGroup.ALL_GROUPS) { - ArrayList listForGroup = new ArrayList<>(); - for (FlightDataType type : types) { - if (type.getGroup().equals(group)) { - listForGroup.add(type); - } - } - subItems.put(group, listForGroup.toArray(new FlightDataType[0])); - } + this.allTypes = types; - mainMenu = createMainMenu(); + initColors(); + + // Create the map of flight data group and corresponding flight data types + typeGroupMap = createFlightDataGroupMap(allGroups, types); + + // Create the search field widget + searchFieldCategory = new PlaceholderTextField(); + searchFieldCategory.setPlaceholder(trans.get("FlightDataComboBox.placeholder")); + searchFieldSearch = new PlaceholderTextField(); + + // Create the filtered list + filteredList = createFilteredList(); + + // Create the different popups + categoryPopup = createCategoryPopup(); + searchPopup = createSearchPopup(); + searchPopup.setPreferredSize(categoryPopup.getPreferredSize()); + + // Add key listener for the search fields + searchFieldCategory.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + overrideActionKeys(e); + } + + public void keyTyped(KeyEvent e) { + EventQueue.invokeLater(() -> { + String text = searchFieldCategory.getText(); + highlightedListIdx = 0; // Start with the first item selected + searchFieldSearch.setText(text); + if (!text.isEmpty() && !searchPopup.isVisible()) { + hideCategoryPopup(); + showSearchPopup(); + filter(text); + } + }); + } + }); + searchFieldSearch.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + overrideActionKeys(e); + } + + @Override + public void keyTyped(KeyEvent e) { + EventQueue.invokeLater(() -> { + String text = searchFieldSearch.getText(); + highlightedListIdx = 0; // Start with the first item selected + searchFieldCategory.setText(text); + if (text.isEmpty() && !categoryPopup.isVisible()) { + hideSearchPopup(); + showCategoryPopup(); + } + filter(text); + }); + } + }); // Override the mouse listeners to use our custom popup for (MouseListener mouseListener : getMouseListeners()) { removeMouseListener(mouseListener); } - addMouseListener(new MouseAdapter() { + addMouseListeners(); + } + + private static void initColors() { + updateColors(); + UITheme.Theme.addUIThemeChangeListener(FlightDataComboBox::updateColors); + } + + private static void updateColors() { + textSelectionBackground = GUIUtil.getUITheme().getTextSelectionBackgroundColor(); + } + + /** + * Create a map of flight data group and corresponding flight data types. + * @param groups the groups + * @param types the types + * @return the map linking the types to their groups + */ + private Hashtable createFlightDataGroupMap(FlightDataTypeGroup[] groups, FlightDataType[] types) { + Hashtable map = new Hashtable<>(); + for (FlightDataTypeGroup group : groups) { + ArrayList listForGroup = new ArrayList<>(); + for (FlightDataType type : types) { + if (type.getGroup().equals(group)) { + listForGroup.add(type); + } + } + map.put(group, listForGroup.toArray(new FlightDataType[0])); + } + + return map; + } + + private JPopupMenu createCategoryPopup() { + final JPopupMenu menu = new JPopupMenu(); + + // Add the search field at the top + menu.add(searchFieldCategory); + menu.addSeparator(); // Separator between search field and menu items + + // Fill the menu with the groups + for (FlightDataTypeGroup group : typeGroupMap.keySet()) { + JMenu groupList = new JMenu(group.getName()); + FlightDataType[] typesForGroup = typeGroupMap.get(group); + + if (typesForGroup != null) { + for (FlightDataType type : typesForGroup) { + JMenuItem typeItem = new JMenuItem(type.getName()); + typeItem.addActionListener(e -> { + setSelectedItem(type); + }); + groupList.add(typeItem); + } + } + + menu.add(groupList); + } + + return menu; + } + + private JPopupMenu createSearchPopup() { + final JPopupMenu menu = new JPopupMenu(); + menu.setLayout(new BorderLayout()); + + // Add the search field at the top + menu.add(searchFieldSearch, BorderLayout.NORTH); + menu.addSeparator(); + + menu.add(new JScrollPane(filteredList)); + + return menu; + } + + private JList createFilteredList() { + JList list = new JList<>(); + list.setCellRenderer(new DefaultListCellRenderer() { @Override - public void mouseClicked(MouseEvent e) { - showCustomPopup(); + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + FlightDataType type = (FlightDataType) value; + String typeName = type.toString(); + + if (typeName.toLowerCase().contains(searchFieldSearch.getText().toLowerCase())) { + // Use HTML to underline matching text + typeName = typeName.replaceAll("(?i)(" + searchFieldSearch.getText() + ")", "$1"); + label.setText("" + typeName + ""); + } + + // Set the hover color + if (highlightedListIdx == index || isSelected) { + label.setBackground(textSelectionBackground); + label.setOpaque(true); + } else { + label.setOpaque(false); + } + + return label; } }); - Component arrowButton = getArrowButton(); - if (arrowButton != null) { - for (MouseListener mouseListener : arrowButton.getMouseListeners()) { - arrowButton.removeMouseListener(mouseListener); - } - arrowButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - showCustomPopup(); + list.addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + Point p = new Point(e.getX(),e.getY()); + int index = list.locationToIndex(p); + if (index != highlightedListIdx) { + highlightedListIdx = index; + list.repaint(); } - }); + } + }); + + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + // Check if the event is in the final phase of change + if (!e.getValueIsAdjusting()) { + selectComboBoxItemFromFilteredList(); + } + } + }); + + return list; + } + + private void selectComboBoxItemFromFilteredList() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + FlightDataType selectedType = filteredList.getSelectedValue(); + if (selectedType != null) { + FlightDataComboBox.this.setSelectedItem(selectedType); + // Hide the popups after selection + hideCategoryPopup(); + hideSearchPopup(); + } + } + }); + } + + private void showCategoryPopup() { + categoryPopup.show(this, 0, getHeight()); + searchFieldSearch.setText(""); + searchFieldCategory.setText(""); + } + + private void hideCategoryPopup() { + categoryPopup.setVisible(false); + } + + private void showSearchPopup() { + searchPopup.show(this, 0, getHeight()); + } + + private void hideSearchPopup() { + searchPopup.setVisible(false); + } + + private void filter(String text) { + filteredList.removeAll(); + String searchText = text.toLowerCase(); + DefaultListModel filteredModel = new DefaultListModel<>(); + + for (FlightDataType item : this.allTypes) { + if (item.toString().toLowerCase().contains(searchText)) { + filteredModel.addElement(item); + } } + + filteredList.setModel(filteredModel); + filteredList.revalidate(); + filteredList.repaint(); } private Component getArrowButton() { @@ -76,30 +314,101 @@ public class FlightDataComboBox extends JComboBox { // Our custom popup will be shown by the MouseListener } - private JPopupMenu createMainMenu() { - JPopupMenu menu = new JPopupMenu(); - - for (FlightDataTypeGroup group : FlightDataTypeGroup.ALL_GROUPS) { - JMenu groupMenu = new JMenu(group.getName()); - FlightDataType[] typesForGroup = subItems.get(group); - - if (typesForGroup != null) { - for (FlightDataType type : typesForGroup) { - JMenuItem typeItem = new JMenuItem(type.getName()); - typeItem.addActionListener(e -> { - setSelectedItem(type); - }); - groupMenu.add(typeItem); - } - } - - menu.add(groupMenu); - } - - return menu; + @Override + public boolean isPopupVisible() { + return categoryPopup.isVisible() || searchPopup.isVisible(); } - private void showCustomPopup() { - mainMenu.show(this, 0, getHeight()); + /** + * Override the default action keys (escape, enter, arrow keys) to do our own actions. + * @param e the key event + */ + private void overrideActionKeys(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + hideCategoryPopup(); + hideSearchPopup(); + } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { + selectHighlightedItemInFilteredList(); + } else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_RIGHT) { + highlightNextItemInFilteredList(); + } else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_LEFT) { + highlightPreviousItemInFilteredList(); + } + } + + /** + * Select the highlighted item in the filtered list and hide the popups. + */ + private void selectHighlightedItemInFilteredList() { + if (highlightedListIdx >= filteredList.getModel().getSize() || highlightedListIdx < 0 || !searchPopup.isVisible()) { + return; + } + filteredList.setSelectedIndex(highlightedListIdx); + selectComboBoxItemFromFilteredList(); + } + + /** + * Highlight the next item in the filtered list. + */ + private void highlightNextItemInFilteredList() { + if (highlightedListIdx + 1 >= filteredList.getModel().getSize() || !searchPopup.isVisible()) { + return; + } + highlightedListIdx++; + filteredList.ensureIndexIsVisible(highlightedListIdx); + filteredList.repaint(); + } + + /** + * Highlight the previous item in the filtered list. + */ + private void highlightPreviousItemInFilteredList() { + if (highlightedListIdx <= 0 || !searchPopup.isVisible()) { + return; + } + highlightedListIdx--; + filteredList.ensureIndexIsVisible(highlightedListIdx); + filteredList.repaint(); + } + + + + /** + * Add mouse listener to widgets of the combobox to open our custom popup menu. + */ + private void addMouseListeners() { + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (!isPopupVisible()) { + showCategoryPopup(); + } + } + }); + } + }); + + Component arrowButton = getArrowButton(); + if (arrowButton != null) { + for (MouseListener mouseListener : arrowButton.getMouseListeners()) { + arrowButton.removeMouseListener(mouseListener); + } + arrowButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (!isPopupVisible()) { + showCategoryPopup(); + } + } + }); + } + }); + } } } diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java index 37e6620bb..99148415b 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java @@ -37,6 +37,7 @@ import net.sf.openrocket.gui.util.UITheme; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightDataTypeGroup; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; @@ -497,7 +498,7 @@ public class SimulationPlotPanel extends JPanel { this.index = plotIndex; - typeSelector = new FlightDataComboBox(types); + typeSelector = new FlightDataComboBox(FlightDataTypeGroup.ALL_GROUPS, types); typeSelector.setSelectedItem(type); typeSelector.addItemListener(new ItemListener() { @Override diff --git a/swing/src/net/sf/openrocket/gui/widgets/PlaceholderTextField.java b/swing/src/net/sf/openrocket/gui/widgets/PlaceholderTextField.java new file mode 100644 index 000000000..ff06bb0c8 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/widgets/PlaceholderTextField.java @@ -0,0 +1,55 @@ +package net.sf.openrocket.gui.widgets; + +import javax.swing.JTextField; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; + +/** + * A text field that can display a placeholder when empty. + * + * @author Sibo Van Gool + */ +public class PlaceholderTextField extends JTextField { + private String placeholder; + + public PlaceholderTextField() { } + + public PlaceholderTextField(final int pColumns) { + super(pColumns); + } + + public PlaceholderTextField(final String pText) { + super(pText); + } + + public PlaceholderTextField(final String pText, final int pColumns) { + super(pText, pColumns); + } + + public String getPlaceholder() { + return placeholder; + } + + + @Override + protected void paintComponent(Graphics pG) { + super.paintComponent(pG); + + if (placeholder == null || placeholder.isEmpty() || !getText().isEmpty()) { + return; + } + + final Graphics2D g = (Graphics2D) pG; + g.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(getDisabledTextColor()); + g.drawString(placeholder, getInsets().left, pG.getFontMetrics() + .getMaxAscent() + getInsets().top); + } + + public void setPlaceholder(final String s) { + placeholder = s; + } +}