📄 thinlet.java
字号:
set(combolist, ":parent", content);
// lay out choices verticaly and calculate max width and height sum
int pw = 0; int ph = 0;
for (Object item = get(combobox, ":comp");
item != null; item = get(item, ":next")) {
Dimension d = getSize(item, 8 , 4);
setRectangle(item, "bounds", 0, ph, d.width, d.height);
pw = Math.max(pw, d.width);
ph += d.height;
}
// set :combolist bounds
int listy = 0, listheight = 0;
int bellow = getRectangle(content, "bounds").height - comboy - comboheight - 1;
if ((ph + 2 > bellow) && (comboy - 1 > bellow)) { // popup above combobox
listy = Math.max(0, comboy - 1 - ph - 2);
listheight = Math.min(comboy - 1, ph + 2);
}
else { // popup bellow combobox
listy = comboy + comboheight + 1;
listheight = Math.min(bellow, ph + 2);
}
setRectangle(combolist, "bounds", combox, listy, combowidth, listheight);
layoutScroll(combolist, pw, ph, 0, 0, 0, 0, true, 0);
repaint(combolist);
// hover the selected item
int selected = getInteger(combobox, "selected", -1);
setInside(combolist, (selected != -1) ? getItem(combobox, selected) : null, true);
return combolist;
}
/**
* @param component menubar or :popup
* @return the created popupmenu
*/
private Object popupMenu(Object component) {
Object popup = get(component, ":popup"); // first :popup child
Object selected = get(component, "selected"); // selected menu in of the component
if (popup != null) { // remove its current :popup
if (get(popup, "menu") == selected) { return null; } // but the currect one
set(popup, "selected", null);
set(popup, "menu", null);
repaint(popup);
removeItemImpl(content, popup);
set(popup, ":parent", null);
set(component, ":popup", null);
if (mouseinside == popup) {
checkLocation();
}
popupMenu(popup); // remove recursively
}
// pop up the selected menu only
if ((selected == null) || (getClass(selected) != "menu")) { return null; }
// create the :popup, :popup.menu -> menu,
// menubar|:popup.:popup -> :popup, menubar|:popup.selected -> menu
popup = createImpl(":popup");
set(popup, "menu", selected);
set(component, ":popup", popup);
insertItem(content, ":comp", popup, 0);
set(popup, ":parent", content);
// calculates the bounds of the previous menubar/:popup relative to the root desktop
int menux = 0, menuy = 0, menuwidth = 0, menuheight = 0;
for (Object comp = component; comp != content; comp = getParent(comp)) {
Rectangle r = getRectangle(comp, "bounds");
menux += r.x; menuy += r.y;
if (comp == component) { menuwidth = r.width; menuheight = r.height; }
}
// set :popup bounds
Rectangle menubounds = getRectangle(selected, "bounds");
boolean menubar = ("menubar" == getClass(component));
if (menubar) { popupowner = component; }
popup(selected, popup,
menubar ? (("bottom" != get(component, "placement")) ? 'D' : 'U') : 'R',
menubar ? (menux + menubounds.x) : menux, menuy + menubounds.y,
menubar ? menubounds.width : menuwidth,
menubar ? menuheight : menubounds.height, menubar ? 1 : 3);
return popup;
}
/**
* @param popupmenu
*/
private void popupPopup(Object popupmenu, int x, int y) {
// :popup.menu -> popupmenu, popupmenu.:popup -> :popup
Object popup = createImpl(":popup");
set(popup, "menu", popupmenu);
set(popupmenu, ":popup", popup);
// add :popup to the root desktop and set the combobox as popupowner
popupowner = popupmenu;
insertItem(content, ":comp", popup, 0);
set(popup, ":parent", content);
// lay out
popup(popupmenu, popup, 'D', x, y, 0, 0, 0);
// invoke menushown listener
invoke(popupmenu, null, "menushown"); // TODO before
}
/**
* Lays out a popupmenu
* @param menu menubar's menu, menu's menu,
* or component's popupmenu including items
* @param popup created popupmenu
* @param direction 'U' for up, 'D' for down, and 'R' for right
* @param x menu's x location relative to the desktop
* @param y menu's y location
* @param width menu's width, or zero for popupmenu
* @param height menu's height
* @param offset inner padding relative to the menu's bounds
*/
private void popup(Object menu, Object popup,
char direction, int x, int y, int width, int height, int offset) {
int pw = 0; int ph = 0;
for (Object item = get(menu, ":comp"); item != null; item = get(item, ":next")) {
String itemclass = getClass(item);
Dimension d = (itemclass == "separator") ? new Dimension(1, 1) :
getSize(item, 8 , 4);
if (itemclass == "checkboxmenuitem") {
d.width = d.width + block + 3;
d.height = Math.max(block, d.height);
}
else if (itemclass == "menu") {
d.width += block;
}
String accelerator = getAccelerator(item); // add accelerator width
if (accelerator != null) {
d.width += 4 + getFontMetrics(font).stringWidth(accelerator); //TODO font, height and gap
}
setRectangle(item, "bounds", 1, 1 + ph, d.width, d.height);
pw = Math.max(pw, d.width);
ph += d.height;
}
pw += 2; ph += 2; // add border widths
// set :popup bounds
Rectangle desktop = getRectangle(content, "bounds");
if (direction == 'R') {
x += ((x + width - offset + pw > desktop.width) &&
(x >= pw - offset)) ? (offset - pw) : (width - offset);
if ((y + ph > desktop.height) && (ph <= y + height)) { y -= ph - height; }
} else {
boolean topspace = (y >= ph - offset); // sufficient space above
boolean bottomspace = (desktop.height - y - height >= ph - offset);
y += ((direction == 'U') ? (topspace || !bottomspace) :
(!bottomspace && topspace)) ? (offset - ph) : (height - offset);
}
setRectangle(popup, "bounds",
Math.max(0, Math.min(x, desktop.width - pw)),
Math.max(0, Math.min(y, desktop.height - ph)), pw, ph);
repaint(popup);
}
/**
* @param item //TODO can be scrollbar string
*/
private void closeCombo(Object combobox, Object combolist, Object item) {
if ((item != null) && getBoolean(item, "enabled", true)) {
String text = getString(item, "text", "");
set(combobox, "text", text); // if editable
putProperty(combobox, "i18n.text", null); // for I18N
setInteger(combobox, "start", text.length(), 0);
setInteger(combobox, "end", 0, 0);
set(combobox, "icon", get(item, "icon"));
validate(combobox);
setInteger(combobox, "selected", getIndex(combobox, item), -1);
invoke(combobox, item, "action");
}
set(combolist, "combobox", null);
set(combobox, ":combolist", null);
removeItemImpl(content, combolist);
repaint(combolist);
set(combolist, ":parent", null);
popupowner = null;
if (mouseinside == combolist) {
checkLocation();
}
}
/**
*
*/
private void closeup() {
if (popupowner != null) {
String classname = getClass(popupowner);
if ("menubar" == classname) {
set(popupowner, "selected", null);
popupMenu(popupowner);
repaint(popupowner); // , selected
}
else if ("combobox" == classname) {
closeCombo(popupowner, get(popupowner, ":combolist"), null);
}
else { // "popupmenu"
popupMenu(popupowner);
}
popupowner = null;
}
}
/**
*
*/
private void showTip() {
String text = null;
tooltipowner = null;
String classname = getClass(mouseinside);
if ((classname == "tabbedpane") || (classname == "menubar") || (classname == ":popup")) {
if (insidepart != null) {
text = getString(insidepart, "tooltip", null);
}
}
else if (classname == ":combolist") {
if (insidepart instanceof Object[]) {
text = getString(insidepart, "tooltip", null);
}
}
// TODO list table tree
if (text == null) { text = getString(mouseinside, "tooltip", null); }
else { tooltipowner = insidepart; }
if (text != null) {
FontMetrics fm = getFontMetrics(font);
int width = fm.stringWidth(text) + 4;
int height = fm.getAscent() + fm.getDescent() + 4;
if (tooltipowner == null) { tooltipowner = mouseinside; }
Rectangle bounds = getRectangle(content, "bounds");
int tx = Math.max(0, Math.min(mousex + 10, bounds.width - width));
int ty = Math.max(0, Math.min(mousey + 10, bounds.height - height));
setRectangle(tooltipowner, ":tooltipbounds", tx, ty, width, height);
repaint(tx, ty, width, height);
}
}
/**
*
*/
private void hideTip() {
if (tooltipowner != null) {
Rectangle bounds = getRectangle(tooltipowner, ":tooltipbounds");
set(tooltipowner, ":tooltipbounds", null);
tooltipowner = null;
repaint(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
/**
*
*/
private void layoutField(Object component, int dw, boolean hidden, int left) {
int width = getRectangle(component, "bounds").width - left -dw;
String text = getString(component, "text", "");
int start = getInteger(component, "start", 0);
if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); }
int end = getInteger(component, "end", 0);
if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); }
int offset = getInteger(component, ":offset", 0);
int off = offset;
Font currentfont = (Font) get(component, "font");
FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
int caret = hidden ? (fm.charWidth('*') * end) :
fm.stringWidth(text.substring(0, end));
if (off > caret) {
off = caret;
}
else if (off < caret - width + 4) {
off = caret - width + 4;
}
off = Math.max(0, Math.min(off, (hidden ? (fm.charWidth('*') *
text.length()) : fm.stringWidth(text)) - width + 4));
if (off != offset) {
setInteger(component, ":offset", off, 0);
}
}
/**
* Set viewport (:port) bounds excluding borders, view position and content
* size (:view), horizontal (:horizontal), and vertical (:vertical) scrollbar
* bounds
*
* @param component scrollable widget
* @param contentwidth preferred component width
* @param contentheight preferred component height
* @param top top inset (e.g. table header, dialog title, half of panel title)
* @param left left inset (e.g. dialog border)
* @param bottom bottom inset (e.g. dialog border)
* @param right right inset (e.g. dialog border)
* @param topgap (lower half of panel title)
* @return true if scrollpane is required, otherwise false
*
* list: 0, 0, 0, 0, true, 0 | table: header, ... | dialog: header, 3, 3, 3, true, 0
* title-border panel: header / 2, 0, 0, 0, true, head
*/
private boolean layoutScroll(Object component,
int contentwidth, int contentheight,
int top, int left, int bottom, int right, boolean border, int topgap) {
Rectangle bounds = getRectangle(component, "bounds");
int iborder = border ? 1 : 0; int iscroll = block + 1 - iborder;
int portwidth = bounds.width - left - right - 2 * iborder; // available horizontal space
int portheight = bounds.height - top - topgap - bottom - 2 * iborder; // vertical space
boolean hneed = contentwidth > portwidth; // horizontal scrollbar required
boolean vneed = contentheight > portheight - (hneed ? iscroll : 0); // vertical scrollbar needed
if (vneed) { portwidth -= iscroll; } // subtract by vertical scrollbar width
hneed = hneed || (vneed && (contentwidth > portwidth));
if (hneed) { portheight -= iscroll; } // subtract by horizontal scrollbar height
setRectangle(component, ":port", left + iborder, top + iborder + topgap, portwidth, portheight);
if (hneed) {
setRectangle(component, ":horizontal", left, bounds.height - bottom - block - 1,
bounds.width - left - right - (vneed ? block : 0), block + 1);
} else { set(component, ":horizontal", null); }
if (vneed) {
setRectangle(component, ":vertical", bounds.width - right - block - 1, top,
block + 1, bounds.height - top - bottom - (hneed ? block : 0));
} else { set(component, ":vertical", null); }
contentwidth = Math.max(contentwidth, portwidth);
contentheight = Math.max(contentheight, portheight);
int viewx = 0, viewy = 0;
Rectangle view = getRectangle(component, ":view");
if (view != null) { // check the previous location
viewx = Math.max(0, Math.min(view.x, contentwidth - portwidth));
viewy = Math.max(0, Math.min(view.y, contentheight - portheight));
}
setRectangle(component, ":view", viewx, viewy, contentwidth, contentheight);
return vneed || hneed;
}
/**
*
*/
private void scrollToVisible(Object component,
int x, int y, int width, int height) {
Rectangle view = getRectangle(component, ":view");
Rectangle port = getRectangle(component, ":port");
int vx = Math.max(x + width - port.width, Math.min(view.x, x));
int vy = Math.max(y + height - port.height, Math.min(view.y, y));
if ((view.x != vx) || (view.y != vy)) {
repaint(component); // horizontal | vertical
view.x = vx; view.y = vy;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -