【发布时间】:2016-11-16 21:21:00
【问题描述】:
我有一个Composite,我希望能够以编程方式启用/禁用它。 Control.setEnabled(boolean enabled) 方法可以正常工作,但它没有提供任何关于小部件被禁用的视觉信息。
我想做的是让禁用状态意味着小部件变灰。现在他们只是进入一个奇怪的状态,用户无法点击或对其执行任何操作。
【问题讨论】:
标签: swt
我有一个Composite,我希望能够以编程方式启用/禁用它。 Control.setEnabled(boolean enabled) 方法可以正常工作,但它没有提供任何关于小部件被禁用的视觉信息。
我想做的是让禁用状态意味着小部件变灰。现在他们只是进入一个奇怪的状态,用户无法点击或对其执行任何操作。
【问题讨论】:
标签: swt
Composite 是一个容器控件,它使用布局保存其他控件 - 您实际上看不到组合,您只能看到它包含的控件。要禁用并在视觉上看到然后禁用,您必须在所有孩子上调用setEnabled(false),假设它们也不是容器。基本上,必须启用/禁用叶子小部件,您会看到视觉指示。
禁用复合时您无法对小部件执行任何操作的原因是复合正在吞噬所有事件。尽管子小部件没有转发事件,但它们对父小部件的状态一无所知,因此它们不会变灰。
【讨论】:
问题确实是我禁用了复合而不是其中的控件。我最终做的是这样的:
public void recursiveSetEnabled(Control ctrl, boolean enabled) {
if (ctrl instanceof Composite) {
Composite comp = (Composite) ctrl;
for (Control c : comp.getChildren())
recursiveSetEnabled(c, enabled);
} else {
ctrl.setEnabled(enabled);
}
}
【讨论】:
此处发布的其他解决方案非常原始。它们有几个缺点:
下面的代码解决了这些问题。 它是 SWT 的最终启用/禁用器。
它通过标记Widget.setData 来跟踪修改后的控件,因此它只启用之前禁用的控件。它在树状态下以不同的方式处理不同类型的控件:DISABLED、READ_ONLY 和 EDITABLE。
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
public class GuiEnabler {
/**
* Used to set the enable state of a tree of controls.
*/
public enum EnableState {
/**
* The control is disabled, for when there is no information to show in
* it. All controls, including labels, are disabled.
*/
DISABLED,
/**
* For when there is information to show in the control, but it should
* be read-only. Controls are disabled, except Text which is
* non-editable, and Lables, which are enabeled.
*/
READ_ONLY,
/**
* All controls are enabled and editable.
*/
EDITABLE
}
private static final String ENABLED_KEY = GuiEnabler.class.getName() + ".disabled";
private static final String EDITABLE_KEY = GuiEnabler.class.getName() + ".read_only";
/**
* Disables or makes read-only {@code control} and all its child controls (recursively).
* Also restores the state of controls previously disabled by this method. The action
* performed on the controls is determined by {@link EnableState enableState}.
*
* @param excluded These controls (and their children) are not modified by
* the method.
*/
public static void recursiveUpdateEnableState(Control control, EnableState enableState, Control... excluded) {
updateEnabledState(control, enableState, new HashSet<>(Arrays.asList(excluded)));
}
/**
* See {@link GuiEnabler#recursiveUpdateEnableState(Control, EnableState, Control...)}.
*/
public static void updateEnabledState(Control control, EnableState enableState, Set<Control> excluded) {
if (excluded.contains(control)) {
return;
} else if (control instanceof ExpandableComposite) {
updateEnabledState(((ExpandableComposite) control).getClient(), enableState, excluded);
} else if (control instanceof Composite && !(control instanceof Combo)) {
for (Control child : ((Composite) control).getChildren()) {
updateEnabledState(child, enableState, excluded);
}
} else {
updateControl(control, enableState);
}
}
/**
* Updates a single control to have its proper state for enableState.
*/
private static void updateControl(Control control, EnableState enableState) {
if (enableState == EnableState.DISABLED) {
makeDisabled(control);
} else if (enableState == EnableState.READ_ONLY) {
if (control instanceof Text) {
makeNonEditable((Text) control);
makeEnabled(control);
} if (control instanceof Label) {
makeEnabled(control);
} else {
makeDisabled(control);
}
} else if (enableState == EnableState.EDITABLE) {
makeEnabled(control);
if (control instanceof Text) makeEditable((Text) control);
}
}
private static void makeEnabled(Control control) {
if (control.getData(ENABLED_KEY) != null) {
control.setData(ENABLED_KEY, null);
control.setEnabled(true);
}
}
private static void makeDisabled(Control control) {
if (control.getEnabled()) {
control.setData(ENABLED_KEY, "marked");
control.setEnabled(false);
}
}
private static void makeEditable(Text text) {
if (text.getData(EDITABLE_KEY) != null) {
text.setData(EDITABLE_KEY, null);
text.setEditable(true);
}
}
private static void makeNonEditable(Text text) {
if (text.getEditable()) {
text.setData(EDITABLE_KEY, "marked");
text.setEditable(false);
}
}
}
这样做的一个限制是,即使在禁用状态下,仍然可以更改 TabFolder 控件中的活动选项卡。
【讨论】:
换句话说,你需要写这样的代码,给定一个Composite c:
for (Control child : c.getChildren())
child.setEnabled(false);
【讨论】: