Kin*_*Dan 3 java user-interface tabs javafx user-experience
我正在用 JavaFX 编写一个将使用选项卡的应用程序。用户可能希望同时打开彼此并排的选项卡(而不是一次只打开一个选项卡)。理想情况下,这可以通过将选项卡拖动到屏幕的一侧来实现。
最初我认为制作一个只有一个网格的网格面板可能是个好主意,然后在用户拖动选项卡时添加网格。我不认为那会奏效。有没有好的方法可以做到这一点?JavaFX 的布局和窗格控件真的如此缺乏吗?
在 C# 中,我可能已经将这样的东西用于我想要的预期行为:http : //avalondock.codeplex.com/
问题:如何在 JavaFX 中使用选项卡实现正确的 UX,允许用户拖动选项卡以具有“拆分”布局?
编辑:目前我正在考虑尝试使用 HBox 复制我的上述想法。有谁知道我如何实现标签拖动到 HBox 中的另一个象限?
所以你的问题有点含糊,我可能不应该回答它,因为老实说我真的不知道“适当的选项卡式界面”真正意味着什么。
这是一个允许选项卡并排打开的系统。右键单击选项卡以显示上下文菜单以选择垂直或水平拆分。选项卡内容将按所需方向在 SplitPane 内的新 TabPane 中复制。
这绝对不是一个完整的对接解决方案,甚至可能不接近您想要的,但也许您会发现它很有用(也许不是;-)。该解决方案只是完成了拆分,而不是在拆分区域之间拖放选项卡(如果您自己无法弄清楚,也许可以与 Tom Schindl 联系以获取有关拖放部分的信息)。
下面的解决方案只是将拆分选项卡的内容复制到新拆分区域中的新选项卡。进一步的增强可以制定类似通用同步文档模型的东西,它允许并行编辑在不同选项卡中复制的文件(类似于 Intellij Idea 的编辑器实现,这种分离系统概念源自于该模型)。
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class SplitTabs extends Application {
final Lorem lorem = new Lorem();
final HashMap<Node, SplitPane> splitPanes = new HashMap<>();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
TabPane tabPane = new TabPane();
//noinspection ResultOfMethodCallIgnored
IntStream.range(0, 5)
.mapToObj(i -> createTab(lorem.nextString(1), lorem.nextString(200)))
.collect(Collectors.toCollection(tabPane::getTabs));
stage.setScene(new Scene(new StackPane(tabPane)));
stage.show();
}
private Tab createTab(String title, String text) {
TextArea textArea = new TextArea(text);
textArea.setWrapText(true);
Tab tab = new Tab(title);
tab.setContent(textArea);
tab.setOnCloseRequest(event -> {
TabPane tabPane = tab.getTabPane();
if (tabPane.getTabs().size() <= 1) {
SplitPane splitPane = splitPanes.get(tabPane);
if (splitPane == null) {
// don't allow the last tab to be closed.
event.consume();
return;
}
int siblingIdx = (splitPane.getItems().indexOf(tabPane) + 1) % 2;
Node siblingItem = splitPane.getItems().get(siblingIdx);
Optional<SplitPane> optionalParentSplitPane =
splitPanes.values().stream()
.filter(searchPane -> searchPane.getItems().contains(splitPane))
.findFirst();
// make the last TabPane the root.
if (!optionalParentSplitPane.isPresent()) {
StackPane stackPane = (StackPane) splitPane.getParent() ;
stackPane.getChildren().setAll(siblingItem);
splitPanes.clear();
return;
}
// graft sibling under parent.
SplitPane parentSplitPane = optionalParentSplitPane.get();
int idx = parentSplitPane.getItems().indexOf(splitPane);
parentSplitPane.getItems().set(idx, siblingItem);
splitPane.getItems().forEach(splitPanes::remove);
splitPanes.put(siblingItem, parentSplitPane);
}
});
MenuItem splitVertically = new MenuItem("Split Vertically");
splitVertically.setOnAction(event -> split(title, text, tab, Orientation.HORIZONTAL));
MenuItem splitHorizontally = new MenuItem("Split Horizontally");
splitHorizontally.setOnAction(event -> {
split(title, text, tab, Orientation.VERTICAL);
});
tab.setContextMenu(new ContextMenu(
splitVertically,
splitHorizontally
));
return tab;
}
private void split(String title, String text, Tab tab, Orientation orientation) {
TabPane tabPane = tab.getTabPane();
Tab tabCopy = createTab(title, text);
TabPane newTabPane = new TabPane(tabCopy);
SplitPane splitPane = new SplitPane(tabPane, newTabPane);
splitPane.setOrientation(orientation);
if (splitPanes.isEmpty()) {
StackPane stackPane = (StackPane) tabPane.getParent();
stackPane.getChildren().setAll(splitPane);
splitPanes.put(tabPane, splitPane);
splitPanes.put(newTabPane, splitPane);
} else {
SplitPane parentSplit = splitPanes.get(tabPane);
int idx = parentSplit.getItems().indexOf(tabPane);
parentSplit.getItems().set(idx, splitPane);
splitPanes.remove(tabPane);
splitPanes.put(splitPane, parentSplit);
splitPanes.put(tabPane, splitPane);
splitPanes.put(newTabPane, splitPane);
}
}
}
class Lorem {
private static final String[] IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque hendrerit imperdiet mi quis convallis. Pellentesque fringilla imperdiet libero, quis hendrerit lacus mollis et. Maecenas porttitor id urna id mollis. Suspendisse potenti. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras lacus tellus, semper hendrerit arcu quis, auctor suscipit ipsum. Vestibulum venenatis ante et nulla commodo, ac ultricies purus fringilla. Aliquam lectus urna, commodo eu quam a, dapibus bibendum nisl. Aliquam blandit a nibh tincidunt aliquam. In tellus lorem, rhoncus eu magna id, ullamcorper dictum tellus. Curabitur luctus, justo a sodales gravida, purus sem iaculis est, eu ornare turpis urna vitae dolor. Nulla facilisi. Proin mattis dignissim diam, id pellentesque sem bibendum sed. Donec venenatis dolor neque, ut luctus odio elementum eget. Nunc sed orci ligula. Aliquam erat volutpat.".split(" ");
private int idx = 0;
public String nextString(int nWords) {
int end = Math.min(idx + nWords, IPSUM.length);
StringBuilder result = new StringBuilder();
for (int i = idx; i < end; i++) {
result.append(IPSUM[i]).append(" ");
}
idx += nWords;
idx = idx % IPSUM.length;
return result.toString();
}
}
Run Code Online (Sandbox Code Playgroud)