📄 cmenu.java
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.widgets.menu;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import edu.tsinghua.lumaqq.ui.helper.UITool;
/**
* 自定义菜单组件
*
* @author luma
*/
public class CMenu {
private List<CMenuItem> items;
private CMenuItem activeItem;
private CMenu parent;
private CMenu activeChild;
private Point location;
private Point size;
private boolean visible;
private boolean sizeDirty;
private Shell menuShell;
private Listener listener;
private int fontHeight;
static final int ITEM_TOP_MARGIN = 1;
static final int ITEM_BOTTOM_MARGIN = 1;
static final int ITEM_LEFT_MARGIN = 5;
static final int ITEM_RIGHT_MARGIN = 5;
static final int IMAGE_TEXT_SPACING = 5;
static final int ARROW = 4;
static final int TEXT_ARROW_SPACING = 10;
static final int VERTICAL_SPACING = 1;
static final int TOP_MARGIN = 3;
static final int BOTTOM_MARGIN = 3;
static final int LEFT_MARGIN = 1;
static final int RIGHT_MARGIN = 1;
static final int SEPARATOR_HEIGHT = 7;
static final int BORDER = 1;
private int imageSize;
private Color background;
private Color textColor;
private Color textHoverColor;
private Color activeBackground;
private Color borderColor;
private Color disabledTextColor;
private static final Color DEFAULT_BACKGROUND = new Color(Display.getCurrent(), 0xE0, 0xEE, 0xFC);
private static final Color DEFAULT_TEXT_COLOR = new Color(Display.getCurrent(), 0x07, 0x1E, 0x81);
private static final Color DEFAULT_TEXT_HOVER_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
private static final Color DEFAULT_ACTIVE_BACKGROUND = new Color(Display.getCurrent(), 0x2E, 0x91, 0xEC);
private static final Color DEFAULT_BORDER_COLOR = new Color(Display.getCurrent(), 0x87, 0xA4, 0xC5);
private static final Color DEFAULT_DISABLED_TEXT_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_GRAY);
private static final Color DARK_SEPARATOR = new Color(Display.getCurrent(), 0xAB, 0xC2, 0xDB);
private static final Color LIGHT_SEPARATOR = new Color(Display.getCurrent(), 0xDF, 0xED, 0xFB);
private Map<String, Object> datas;
private static final String DEFAULT_DATA = "default_data";
private List<IMenuListener> listeners;
// 是否做双缓冲
private static final boolean DOUBLE_BUFFERED;
static {
// 如果不是Mac,底层也不是gtk,则做双缓冲
String platform = SWT.getPlatform();
DOUBLE_BUFFERED = !"carbon".equals(platform);
}
public CMenu(CMenuItem item) {
item.setChild(this);
items = new ArrayList<CMenuItem>();
imageSize = 16;
fontHeight = UITool.calculateDefaultFontHeight(new GC(Display.getDefault()));
initColors();
}
public CMenu() {
items = new ArrayList<CMenuItem>();
imageSize = 16;
fontHeight = UITool.calculateDefaultFontHeight(new GC(Display.getDefault()));
initColors();
}
private void initColors() {
background = DEFAULT_BACKGROUND;
textColor = DEFAULT_TEXT_COLOR;
textHoverColor = DEFAULT_TEXT_HOVER_COLOR;
activeBackground = DEFAULT_ACTIVE_BACKGROUND;
borderColor = DEFAULT_BORDER_COLOR;
disabledTextColor = DEFAULT_DISABLED_TEXT_COLOR;
}
public void addMenuListener(IMenuListener ml) {
if(listeners == null)
listeners = new ArrayList<IMenuListener>();
listeners.add(ml);
}
void fireMenuShownEvent() {
if(listeners == null)
return;
MenuEvent e = new MenuEvent(this);
for(IMenuListener ml : listeners)
ml.menuShown(e);
}
public void setData(Object data) {
if(datas == null)
datas = new HashMap<String, Object>();
datas.put(DEFAULT_DATA, data);
}
public void setData(String key, Object data) {
if(datas == null)
datas = new HashMap<String, Object>();
datas.put(key, data);
}
public Object getData() {
if(datas == null)
return null;
else
return datas.get(DEFAULT_DATA);
}
public Object getData(String key) {
if(datas == null)
return null;
else
return datas.get(key);
}
void addItem(CMenuItem item) {
items.add(item);
}
void addItem(CMenuItem item, int index) {
items.add(index, item);
}
/**
* 显示菜单
*/
void show() {
fireMenuShownEvent();
if(menuShell != null && !menuShell.isDisposed()) {
// 检查菜单大小是否需要重新计算
if(sizeDirty)
computeSize();
// 调整菜单位置,显示
adjustShellLocation();
if(!menuShell.getVisible())
menuShell.setVisible(true);
} else {
menuShell = new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
listener = new Listener() {
public void handleEvent(Event e) {
switch(e.type) {
case SWT.Paint:
handlePaint(e);
break;
case SWT.MouseMove:
handleMouseMove(e);
break;
case SWT.MouseUp:
handleMouseUp(e);
break;
case SWT.Dispose:
handleDispose(e);
break;
}
}
};
menuShell.addListener(SWT.Paint, listener);
menuShell.addListener(SWT.MouseMove, listener);
menuShell.addListener(SWT.Dispose, listener);
menuShell.addListener(SWT.MouseUp, listener);
computeSize();
if(location == null)
location = new Point(0, 0);
adjustShellLocation();
menuShell.setSize(size);
menuShell.setVisible(true);
menuShell.redraw();
}
}
/**
* 调整菜单位置
*/
private void adjustShellLocation() {
Rectangle displayBound = menuShell.getDisplay().getBounds();
int shellX, shellY;
int rightExceed = location.x + size.x - displayBound.width;
int leftExceed = - (location.x - size.x);
int topExceed = - (location.y - size.y);
int bottomExceed = location.y + size.y - displayBound.height;
if(rightExceed <= 0 || rightExceed <= leftExceed)
shellX = location.x;
else
shellX = -leftExceed;
if(bottomExceed <= 0 || bottomExceed <= topExceed)
shellY = location.y;
else
shellY = -topExceed;
menuShell.setLocation(shellX, shellY);
}
/**
* 隐藏菜单
*/
void hide() {
hide(true);
}
/**
* 隐藏菜单,可选是否也隐藏上一级菜单
*
* @param hideParent
*/
void hide(boolean hideParent) {
hideSelf();
if(parent != null && hideParent)
parent.hide(hideParent);
if(activeChild != null)
activeChild.hideSelf();
}
void hideSelf() {
visible = false;
if(menuShell != null && !menuShell.isDisposed()) {
menuShell.setVisible(false);
menuShell.dispose();
menuShell = null;
}
}
/**
* @return
* 菜单大小
*/
public Point getSize() {
if(size == null)
computeSize();
return size;
}
public int getItemCount() {
return items.size();
}
public CMenuItem[] getItems() {
return items.toArray(new CMenuItem[getItemCount()]);
}
public CMenuItem getItem(int index) {
return items.get(index);
}
void removeItem(CMenuItem item) {
items.remove(item);
}
/**
* 菜单的大小
*/
protected void computeSize() {
if(size == null)
size = new Point(0, 0);
GC gc = new GC(Display.getCurrent());
int maxTextWidth = 0;
int itemHeight = getItemHeight();
size.x = LEFT_MARGIN + RIGHT_MARGIN + imageSize + IMAGE_TEXT_SPACING + ITEM_LEFT_MARGIN + ITEM_RIGHT_MARGIN + BORDER + BORDER + ARROW + TEXT_ARROW_SPACING;
size.y = TOP_MARGIN + BOTTOM_MARGIN + BORDER + BORDER;
for(CMenuItem item : items) {
if(!item.isVisible())
continue;
if(item.getStyle() == SWT.SEPARATOR)
size.y += SEPARATOR_HEIGHT + VERTICAL_SPACING;
else {
size.y += itemHeight + VERTICAL_SPACING;
maxTextWidth = Math.max(maxTextWidth, gc.textExtent(item.getText()).x);
}
}
size.x += maxTextWidth;
size.y -= VERTICAL_SPACING;
gc.dispose();
sizeDirty = false;
}
protected void handleDispose(Event e) {
menuShell.removeListener(SWT.Dispose, listener);
}
protected void handleMouseUp(Event e) {
if(activeItem != null) {
if(activeItem.isEnabled()) {
switch(activeItem.getStyle()) {
case SWT.PUSH:
setVisible(false);
activeItem.fireSelectionEvent();
break;
case SWT.CHECK:
setVisible(false);
activeItem.setSelected(!activeItem.isSelected());
activeItem.fireSelectionEvent();
break;
case SWT.RADIO:
setVisible(false);
activeItem.setSelected(!activeItem.isSelected());
unselect(activeItem);
activeItem.fireSelectionEvent();
break;
}
}
} else
setVisible(false);
}
/**
* 取消其他radio菜单的选择状态
*
* @param except
* 除了这个之外
*/
private void unselect(CMenuItem except) {
int index = items.indexOf(except);
if(index == -1)
return;
int size = getItemCount();
for(int i = index + 1; i < size; i++) {
CMenuItem item = getItem(i);
if(item.getStyle() == SWT.RADIO)
item.setSelected(false);
else if(item.getStyle() == SWT.SEPARATOR)
break;
}
for(int i = index - 1; i >= 0; i--) {
CMenuItem item = getItem(i);
if(item.getStyle() == SWT.RADIO)
item.setSelected(false);
else if(item.getStyle() == SWT.SEPARATOR)
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -