Add tooltip support to config editor via _TIP convention
ObjectEditorPanel now looks up a static String constant named FIELD_NAME_TIP (e.g. myField → MY_FIELD_TIP) on the ConfigObject after calling getEditor(), and sets it as a tooltip on the editor panel. No changes to ConfigObject's API — existing getEditor() overrides continue to work and automatically receive tooltips. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.swing.ScrollablePanel;
|
||||
import com.samskivert.swing.VGroupLayout;
|
||||
@@ -89,7 +90,7 @@ public class ObjectEditorPanel extends ScrollablePanel
|
||||
// if the field is anything but a plain old public field,
|
||||
// we don't want to edit it
|
||||
if (field.getModifiers() == Modifier.PUBLIC) {
|
||||
add(_object.getEditor(_ctx, field));
|
||||
add(applyTip(object, field, _object.getEditor(_ctx, field)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +107,31 @@ public class ObjectEditorPanel extends ScrollablePanel
|
||||
log.warning("Unable to subscribe to config object: " + cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a {@code FIELD_NAME_TIP} static String constant on the config object and applies
|
||||
* it as a tooltip on the editor panel.
|
||||
*/
|
||||
protected static JPanel applyTip (ConfigObject object, Field field, JPanel editor)
|
||||
{
|
||||
String tipName = toUpperSnake(field.getName()) + "_TIP";
|
||||
try {
|
||||
Field tipField = object.getClass().getField(tipName);
|
||||
if (tipField.getType() == String.class &&
|
||||
Modifier.isStatic(tipField.getModifiers())) {
|
||||
editor.setToolTipText((String) tipField.get(null));
|
||||
}
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
// no tip constant for this field
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
/** Converts a {@code lowerCamelCase} name to {@code UPPER_SNAKE_CASE}. */
|
||||
private static String toUpperSnake (String name)
|
||||
{
|
||||
return name.replaceAll("([a-z0-9])([A-Z])", "$1_$2").toUpperCase();
|
||||
}
|
||||
|
||||
protected PresentsContext _ctx;
|
||||
protected String _key;
|
||||
protected SafeSubscriber<ConfigObject> _safesub;
|
||||
|
||||
@@ -870,11 +870,7 @@ public class Client
|
||||
}
|
||||
// otherwise queue this notification up to run on the run queue thread
|
||||
else {
|
||||
_runQueue.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
_observers.apply(op);
|
||||
}
|
||||
});
|
||||
_runQueue.postRunnable(() -> _observers.apply(op));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,38 +890,36 @@ public class Client
|
||||
// CLIENT_DID_LOGOFF; that may not have been invoked yet, so we don't want to clear out our
|
||||
// communicator reference immediately; instead we queue up a runnable unit to do so to
|
||||
// ensure that it won't happen until CLIENT_DID_LOGOFF was dispatched
|
||||
_runQueue.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
// tell the object manager that we're no longer connected to the server
|
||||
if (_omgr instanceof ClientDObjectMgr) {
|
||||
((ClientDObjectMgr)_omgr).cleanup();
|
||||
}
|
||||
|
||||
// clear out our references
|
||||
_comm = null;
|
||||
_bstrap = null;
|
||||
_omgr = null;
|
||||
_clobj = null;
|
||||
_connectionId = _cloid = -1;
|
||||
_standalone = false;
|
||||
|
||||
// and let our invocation director know we're logged off
|
||||
_invdir.cleanup();
|
||||
|
||||
// if we were cleaned up due to a failure to logon, we can report the logon error
|
||||
// now that the communicator is cleaned up; this allows a logon failure listener to
|
||||
// immediately try another logon (hopefully with something changed like the server
|
||||
// or port)
|
||||
notifyObservers(new ObserverOps.Client(Client.this) {
|
||||
@Override protected void notify (ClientObserver obs) {
|
||||
if (logonError != null) {
|
||||
obs.clientFailedToLogon(_client, logonError);
|
||||
} else {
|
||||
obs.clientDidClear(_client);
|
||||
}
|
||||
}
|
||||
});
|
||||
_runQueue.postRunnable(() -> {
|
||||
// tell the object manager that we're no longer connected to the server
|
||||
if (_omgr instanceof ClientDObjectMgr) {
|
||||
((ClientDObjectMgr)_omgr).cleanup();
|
||||
}
|
||||
|
||||
// clear out our references
|
||||
_comm = null;
|
||||
_bstrap = null;
|
||||
_omgr = null;
|
||||
_clobj = null;
|
||||
_connectionId = _cloid = -1;
|
||||
_standalone = false;
|
||||
|
||||
// and let our invocation director know we're logged off
|
||||
_invdir.cleanup();
|
||||
|
||||
// if we were cleaned up due to a failure to logon, we can report the logon error
|
||||
// now that the communicator is cleaned up; this allows a logon failure listener to
|
||||
// immediately try another logon (hopefully with something changed like the server
|
||||
// or port)
|
||||
notifyObservers(new ObserverOps.Client(Client.this) {
|
||||
@Override protected void notify (ClientObserver obs) {
|
||||
if (logonError != null) {
|
||||
obs.clientFailedToLogon(_client, logonError);
|
||||
} else {
|
||||
obs.clientDidClear(_client);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -234,10 +234,8 @@ public class ClientDObjectMgr
|
||||
_flusher.cancel();
|
||||
_flushes.clear();
|
||||
_dead.clear();
|
||||
_client.getRunQueue().postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
_ocache.clear();
|
||||
}
|
||||
_client.getRunQueue().postRunnable(() -> {
|
||||
_ocache.clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user