🐛 修复文件树由于缓存问题导致新增的文件无法被显示 🚩初步添加文件树菜单

This commit is contained in:
gewuyou 2023-10-24 18:50:25 +08:00
parent 9037223584
commit 2c3416fde5
13 changed files with 279 additions and 74 deletions

View File

@ -76,6 +76,23 @@ public abstract class AbstractMenuBuilder<B, T> {
return getBuilder(); return getBuilder();
} }
/**
* 添加菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @param visible 是否可见
* @return 建造者
*/
public B addMenuItem(String label, EventHandler<ActionEvent> eventHandler, boolean visible) {
MenuItem menuItem = new MenuItem(label);
menuItem.setOnAction(eventHandler);
menuItem.setVisible(visible);
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
/** /**
* 添加单选菜单项 * 添加单选菜单项
* *
@ -141,6 +158,20 @@ public abstract class AbstractMenuBuilder<B, T> {
return getBuilder(); return getBuilder();
} }
/**
* 添加菜单
*
* @param menu 菜单
* @param visible 是否隐藏
* @return 建造者
*/
public B addMenu(Menu menu, boolean visible) {
menu.setVisible(visible);
menuItems.put(menu.getText(), menu);
getItems().add(menu);
return getBuilder();
}
/** /**
* 添加分割线 * 添加分割线
@ -166,6 +197,19 @@ public abstract class AbstractMenuBuilder<B, T> {
return getBuilder(); return getBuilder();
} }
/**
* 添加分割线
*
* @param visible 是否可见
* @return 建造者
*/
public B addSeparatorMenuItem(boolean visible) {
SeparatorMenuItem separatorMenuItem = new SeparatorMenuItem();
separatorMenuItem.setVisible(visible);
getItems().add(separatorMenuItem);
return getBuilder();
}
/** /**
* Build menu * Build menu
* *

View File

@ -23,6 +23,10 @@ public class TextConstants {
* 文件文本常量 * 文件文本常量
*/ */
public static final String FILE = "FILE"; public static final String FILE = "FILE";
/**
* 文件夹
*/
public static final String FOLDER = "FOLDER";
/** /**
* 构建文本常量 * 构建文本常量
@ -139,6 +143,16 @@ public class TextConstants {
*/ */
public static final String NEW_FILE = "NEW_FILE"; public static final String NEW_FILE = "NEW_FILE";
/**
* 新建文件夹
*/
public static final String NEW_DIRECTORY = "NEW_DIRECTORY";
/**
* 删除
*/
public static final String DELETE = "DELETE";
/** /**
* 行文本常量 * 行文本常量
*/ */

View File

@ -37,6 +37,8 @@ public class FileUtil {
private static final String MAC = "mac"; private static final String MAC = "mac";
private static final String PATH = "path";
static { static {
try { try {
MESSAGE_DIGEST_SHA_256 = MessageDigest.getInstance("SHA-256"); MESSAGE_DIGEST_SHA_256 = MessageDigest.getInstance("SHA-256");
@ -189,9 +191,10 @@ public class FileUtil {
if (Objects.isNull(dirFileModels) || dirFileModels.isEmpty()) { if (Objects.isNull(dirFileModels) || dirFileModels.isEmpty()) {
return null; return null;
} }
File rootDir = new File((String) dirFileModels.get(PATH));
DirFileModel dirFileModel = new DirFileModel( DirFileModel dirFileModel = new DirFileModel(
(String) dirFileModels.get("path"), rootDir.getAbsolutePath(),
(String) dirFileModels.get("name"), new ArrayList<>(), rootDir.getName(), new ArrayList<>(),
new FontIcon(FOLDER), new FontIcon(FOLDER),
new FontIcon(FOLDER_OPEN), (Boolean) dirFileModels.get("open")); new FontIcon(FOLDER_OPEN), (Boolean) dirFileModels.get("open"));
Optional<Object> o = Optional.ofNullable(dirFileModels.get("childFile")); Optional<Object> o = Optional.ofNullable(dirFileModels.get("childFile"));
@ -199,16 +202,29 @@ public class FileUtil {
return null; return null;
} }
List<Map<String, Object>> childFile = (List<Map<String, Object>>) o.get(); List<Map<String, Object>> childFile = (List<Map<String, Object>>) o.get();
for (Map<String, Object> map : childFile) { File[] files = rootDir.listFiles();
Object obj = map.get("childFile"); if (files == null) {
if (obj == null) { return null;
dirFileModel.getChildFile().add(new DirFileModel( }
(String) map.get("path"), (String) map.get("name"), null,
getIconCorrespondingToFileName((String) map.get("name")), for (File f : files) {
null)); if (f.isDirectory()) {
Optional<Map<String, Object>> first = childFile
.stream()
.filter(map -> map.get(PATH).equals(f.getAbsolutePath())).findFirst();
DirFileModel childDirFileModel;
if (first.isPresent()) {
childDirFileModel = getDirFileModel(first.get());
} else { } else {
DirFileModel childDirFileModel = getDirFileModel(map); childDirFileModel = getDirFileModel(f);
}
dirFileModel.getChildFile().add(childDirFileModel); dirFileModel.getChildFile().add(childDirFileModel);
} else {
// 在此监测文件后缀设置对应的图标
dirFileModel.getChildFile().add(new DirFileModel(
f.getAbsolutePath(), f.getName(), null,
getIconCorrespondingToFileName(f.getName()),
null));
} }
} }
return dirFileModel; return dirFileModel;
@ -365,17 +381,18 @@ public class FileUtil {
* @param folder the folder in which to open the terminal * @param folder the folder in which to open the terminal
*/ */
public static void openTerminal(File folder) { public static void openTerminal(File folder) {
if (folder.exists() && folder.isDirectory()) { if (!folder.exists()) {
return;
}
if (folder.isFile()) {
folder = folder.getParentFile();
}
String os = System.getProperty("os.name").toLowerCase(); String os = System.getProperty("os.name").toLowerCase();
ProcessBuilder processBuilder = getProcessBuilder(folder, os); ProcessBuilder processBuilder = getProcessBuilder(folder, os);
try { try {
processBuilder.start(); processBuilder.start();
} catch (IOException e) { } catch (IOException e) {
PopUpUtil.errorAlert("打开失败", "打开于终端失败", "错误原因" + e.getMessage(), null, null); PopUpUtil.errorAlert("打开失败", "打开于终端失败", "错误原因:" + e.getMessage(), null, null);
}
} else {
logger.info("文件夹不存在或者不是文件夹");
} }
} }
@ -400,5 +417,18 @@ public class FileUtil {
} }
return processBuilder; return processBuilder;
} }
/**
* Creates a file at the specified path.
*
* @param path The path to the file to be created.
*/
public static void createFile(Path path) {
try {
Files.createFile(path);
} catch (IOException e) {
logger.error("创建文件失败", e);
}
}
} }

View File

@ -128,19 +128,18 @@ public class TabUtil {
* @param tab 标签页组件 * @param tab 标签页组件
*/ */
private static void handleRenameTab(CenterTab tab) { private static void handleRenameTab(CenterTab tab) {
TextField textField = new TextField(tab.getText());
textField.getStyleClass().add("tab-title-editable");
// 临时记录标签页名称 // 临时记录标签页名称
String tempName = tab.getText(); String tempName = tab.getText();
TextField textField = new TextField(tempName);
textField.getStyleClass().add("tab-title-editable");
// 清空标签页名称 // 清空标签页名称
tab.setText(""); tab.setText("");
// 监听 Enter 完成编辑 // 监听 Enter 完成编辑
textField.setOnKeyPressed(event -> { textField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) { if (event.getCode() == KeyCode.ENTER) {
String newTabName = textField.getText(); String newTabName = textField.getText();
// 检查是否存在相同名称的标签页 // 检查是否存在相同名称的标签页
if (isTabNameExists(newTabName)) { if (tabNameExists(newTabName)) {
// 显示弹窗并提示用户更换名称 // 显示弹窗并提示用户更换名称
showDuplicateNameAlert(newTabName); showDuplicateNameAlert(newTabName);
@ -162,7 +161,7 @@ public class TabUtil {
textField.focusedProperty().addListener((observable, oldValue, newValue) -> { textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
String newTabName = textField.getText(); String newTabName = textField.getText();
// 检查是否存在相同名称的标签页 // 检查是否存在相同名称的标签页
if (isTabNameExists(newTabName)) { if (tabNameExists(newTabName)) {
// 恢复原始名称 // 恢复原始名称
tab.setText(tempName); tab.setText(tempName);
@ -191,7 +190,7 @@ public class TabUtil {
* @param newTabName 要检查的新标签页名称 * @param newTabName 要检查的新标签页名称
* @return 如果存在具有相同名称的标签页则返回 true否则返回 false * @return 如果存在具有相同名称的标签页则返回 true否则返回 false
*/ */
private static boolean isTabNameExists(String newTabName) { private static boolean tabNameExists(String newTabName) {
CenterTabPane tabPane = CenterTabPane.getInstance(); CenterTabPane tabPane = CenterTabPane.getInstance();
return tabPane.getTabs().stream() return tabPane.getTabs().stream()
.anyMatch(tab -> tab.getText().equals(newTabName)); .anyMatch(tab -> tab.getText().equals(newTabName));
@ -245,6 +244,22 @@ public class TabUtil {
public static void addNewFileTab() { public static void addNewFileTab() {
// 创建一个新的文本编辑区 // 创建一个新的文本编辑区
TextCodeArea textArea = new TextCodeArea(); TextCodeArea textArea = new TextCodeArea();
// 创建标签页
CenterTab centerTab = new CenterTab(
generateDefaultName(),
textArea);
// 将Tab页添加到TabPane中
CenterTabPaneManager.getInstance().addNewTab(centerTab);
// 更新编码信息
BottomStatusBoxManager.getInstance().updateEncodingLabel();
}
/**
* Generate the default name for a new tab.
*
* @return The default name for a new tab.
*/
private static String generateDefaultName() {
// 设定初始索引 // 设定初始索引
int index = 1; int index = 1;
StringBuilder tabTitle = new StringBuilder(); StringBuilder tabTitle = new StringBuilder();
@ -275,14 +290,7 @@ public class TabUtil {
break; break;
} }
} }
// 创建标签页 return tabTitle.toString();
CenterTab centerTab = new CenterTab(
tabTitle.toString(),
textArea);
// 将Tab页添加到TabPane中
CenterTabPaneManager.getInstance().addNewTab(centerTab);
// 更新编码信息
BottomStatusBoxManager.getInstance().updateEncodingLabel();
} }
/** /**

View File

@ -51,9 +51,13 @@ public class OpenDirectory implements EventHandler<ActionEvent> {
CACHE_MANAGER.addCache(cache); CACHE_MANAGER.addCache(cache);
} }
flushDirSidebar(file); flushDirSidebar(file);
} }
/**
* Flushes the directory sidebar with the given file.
*
* @param file the file to be converted into an entity class
*/
public void flushDirSidebar(File file) { public void flushDirSidebar(File file) {
// 将文件转为实体类 // 将文件转为实体类
DirFileModel dirFileModel = FileUtil.getDirFileModel(file); DirFileModel dirFileModel = FileUtil.getDirFileModel(file);

View File

@ -23,6 +23,7 @@ public class DirFileModel {
/** /**
* 文件名 * 文件名
*/ */
@JsonIgnore
private String name; private String name;
/** /**
@ -45,13 +46,6 @@ public class DirFileModel {
*/ */
private boolean isOpen; private boolean isOpen;
public DirFileModel(String path, String name, List<DirFileModel> childFile, boolean isOpen) {
this.path = path;
this.name = name;
this.childFile = childFile;
this.isOpen = isOpen;
}
public DirFileModel(String path, String name, List<DirFileModel> childFile, Node iconIsNotSelected, Node iconIsSelected) { public DirFileModel(String path, String name, List<DirFileModel> childFile, Node iconIsNotSelected, Node iconIsSelected) {
this.path = path; this.path = path;
this.name = name; this.name = name;

View File

@ -103,13 +103,13 @@ public class DirectorySidebarManager {
*/ */
public void setTreeView(DirFileModel dirFileModel) { public void setTreeView(DirFileModel dirFileModel) {
TreeItem<DirFileModel> rootItem = new TreeItem<>(dirFileModel, dirFileModel.getIconIsNotSelected()); TreeItem<DirFileModel> rootItem = new TreeItem<>(dirFileModel, dirFileModel.getIconIsNotSelected());
DIRECTORY_SIDEBAR_PANE.setRoot(rootItem); DIRECTORY_SIDEBAR_PANE.setRoot(rootItem);
rootItem.expandedProperty().addListener(getTreeItemListener(rootItem)); rootItem.expandedProperty().addListener(getTreeItemListener(rootItem));
rootItem.setExpanded(dirFileModel.isOpen()); rootItem.setExpanded(dirFileModel.isOpen());
expandFolder(dirFileModel, rootItem); expandFolder(dirFileModel, rootItem);
} }
/** /**
* 递归展开 dirFileModel * 递归展开 dirFileModel
* *

View File

@ -0,0 +1,74 @@
package org.jcnc.jnotepad.ui.views.manager;
import javafx.scene.control.Menu;
import org.jcnc.jnotepad.api.core.views.menu.builder.MenuBuilder;
import org.jcnc.jnotepad.app.utils.ClipboardUtil;
import org.jcnc.jnotepad.app.utils.FileUtil;
import org.jcnc.jnotepad.app.utils.NotificationUtil;
import java.io.File;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.*;
/**
* 菜单管理类
*
* @author gewuyou
*/
public class MenuManager {
private MenuManager() {
}
/**
* Generates a copy menu for the given file.
*
* @param file the file to generate the copy menu for
* @return the generated copy menu
*/
public static Menu getCopyMenu(File file) {
return new MenuBuilder(COPY)
.addMenuItem(FILE_NAME, e -> {
ClipboardUtil.writeTextToClipboard(file.getName());
NotificationUtil.infoNotification("已复制文件名!");
})
.addMenuItem(FILE_PATH, e -> {
ClipboardUtil.writeTextToClipboard(file.getAbsolutePath());
NotificationUtil.infoNotification("已复制文件路径!");
})
.addMenuItem(FOLDER_PATH, e -> {
ClipboardUtil.writeTextToClipboard(file.getParent());
NotificationUtil.infoNotification("已复制所在文件夹!");
})
.build();
}
/**
* Retrieves the open menu for a given file.
*
* @param file the file for which to retrieve the open menu
* @return the open menu for the given file
*/
public static Menu getOpenMenu(File file) {
return new MenuBuilder(OPEN_ON)
.addMenuItem(EXPLORER, e -> FileUtil.openExplorer(file))
.addMenuItem(TERMINAL, e -> FileUtil.openTerminal(file))
.build();
}
/**
* Returns a new Menu.
*
* @return a new Menu object
*/
public static Menu getNewMenu() {
return new MenuBuilder(NEW)
.addMenuItem(FILE, e -> {
})
.addMenuItem(FOLDER, e -> {
})
.build();
}
}

View File

@ -1,12 +1,19 @@
package org.jcnc.jnotepad.ui.views.root.center.main.center.directory; package org.jcnc.jnotepad.ui.views.root.center.main.center.directory;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView; import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import org.jcnc.jnotepad.api.core.views.menu.builder.ContextMenuBuilder;
import org.jcnc.jnotepad.model.entity.DirFileModel; import org.jcnc.jnotepad.model.entity.DirFileModel;
import org.jcnc.jnotepad.ui.views.manager.DirectorySidebarManager;
import org.jcnc.jnotepad.ui.views.manager.MenuManager;
import java.io.File; import java.io.File;
import java.util.Objects; import java.util.Objects;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.OPEN;
import static org.jcnc.jnotepad.app.utils.TabUtil.openFileToTab; import static org.jcnc.jnotepad.app.utils.TabUtil.openFileToTab;
@ -23,23 +30,64 @@ public class DirectorySidebarPane extends TreeView<DirFileModel> {
private static final int CLICK_COUNT = 2; private static final int CLICK_COUNT = 2;
private final DirectorySidebarManager directorySidebarManager = DirectorySidebarManager.getInstance();
private ContextMenu contextMenu;
private DirectorySidebarPane() { private DirectorySidebarPane() {
initMouseClickEvent();
}
/**
* Initializes the mouse click event for the Java function.
*/
private void initMouseClickEvent() {
this.setOnMouseClicked(mouseEvent -> { this.setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getClickCount() == CLICK_COUNT) {
TreeItem<DirFileModel> item = DirectorySidebarPane.this.getSelectionModel().getSelectedItem(); TreeItem<DirFileModel> item = DirectorySidebarPane.this.getSelectionModel().getSelectedItem();
if (Objects.isNull(item)) { if (Objects.isNull(item)) {
return; return;
} }
if (mouseEvent.getClickCount() == CLICK_COUNT) {
File file = new File(item.getValue().getPath()); File file = new File(item.getValue().getPath());
if (!file.isFile()) { if (!file.isFile()) {
return; return;
} }
openFileToTab(file); openFileToTab(file);
} }
if (!Objects.isNull(contextMenu)) {
contextMenu.hide();
}
if (mouseEvent.getButton() == MouseButton.SECONDARY) {
updateContextMenu(mouseEvent, item);
}
}); });
} }
private void updateContextMenu(MouseEvent mouseEvent, TreeItem<DirFileModel> item) {
createContextMenu(item);
contextMenu.show(this, mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
private void createContextMenu(TreeItem<DirFileModel> item) {
ContextMenuBuilder builder = new ContextMenuBuilder();
boolean isFile = !DirFileModel.isDirectoryByDirFileModel(item.getValue());
File file = new File(item.getValue().getPath());
contextMenu = builder
.addMenuItem(OPEN, e -> openFileToTab(file), isFile)
.addSeparatorMenuItem(isFile)
.addMenu(MenuManager.getNewMenu(), !isFile)
// .addSeparatorMenuItem()
// .addMenuItem(RENAME, e -> directorySidebarManager.rename(file))
// .addMenuItem(DELETE, e -> directorySidebarManager.delete(file))
// .addSeparatorMenuItem(isFile)
.addMenu(MenuManager.getCopyMenu(file), isFile)
.addSeparatorMenuItem(isFile)
.addMenu(MenuManager.getOpenMenu(file))
.build();
}
public static DirectorySidebarPane getInstance() { public static DirectorySidebarPane getInstance() {
return INSTANCE; return INSTANCE;
} }

View File

@ -8,12 +8,14 @@ import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import org.fxmisc.flowless.VirtualizedScrollPane; import org.fxmisc.flowless.VirtualizedScrollPane;
import org.jcnc.jnotepad.api.core.views.menu.builder.ContextMenuBuilder; import org.jcnc.jnotepad.api.core.views.menu.builder.ContextMenuBuilder;
import org.jcnc.jnotepad.api.core.views.menu.builder.MenuBuilder; import org.jcnc.jnotepad.app.utils.FileUtil;
import org.jcnc.jnotepad.app.utils.*; import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.app.utils.TabUtil;
import org.jcnc.jnotepad.controller.config.UserConfigController; import org.jcnc.jnotepad.controller.config.UserConfigController;
import org.jcnc.jnotepad.ui.component.module.TextCodeArea; import org.jcnc.jnotepad.ui.component.module.TextCodeArea;
import org.jcnc.jnotepad.ui.views.manager.BottomStatusBoxManager; import org.jcnc.jnotepad.ui.views.manager.BottomStatusBoxManager;
import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager; import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager;
import org.jcnc.jnotepad.ui.views.manager.MenuManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -163,20 +165,7 @@ public class CenterTab extends Tab {
) )
.addSeparatorMenuItem(this.relevancePropertyProperty()) .addSeparatorMenuItem(this.relevancePropertyProperty())
.addMenu( .addMenu(
new MenuBuilder(COPY) MenuManager.getCopyMenu(file)
.addMenuItem(FILE_NAME, e -> {
ClipboardUtil.writeTextToClipboard(file.getName());
NotificationUtil.infoNotification("已复制文件名!");
})
.addMenuItem(FILE_PATH, e -> {
ClipboardUtil.writeTextToClipboard(file.getAbsolutePath());
NotificationUtil.infoNotification("已复制文件路径!");
})
.addMenuItem(FOLDER_PATH, e -> {
ClipboardUtil.writeTextToClipboard(file.getParent());
NotificationUtil.infoNotification("已复制所在文件夹!");
})
.build()
, this.relevancePropertyProperty() , this.relevancePropertyProperty()
) )
.addSeparatorMenuItem() .addSeparatorMenuItem()
@ -184,10 +173,7 @@ public class CenterTab extends Tab {
.addMenuItem(SAVE_AS, e -> TabUtil.saveAsFile(this), this.relevancePropertyProperty()) .addMenuItem(SAVE_AS, e -> TabUtil.saveAsFile(this), this.relevancePropertyProperty())
.addMenuItem(RENAME, e -> TabUtil.rename(this)) .addMenuItem(RENAME, e -> TabUtil.rename(this))
.addSeparatorMenuItem(this.relevancePropertyProperty()) .addSeparatorMenuItem(this.relevancePropertyProperty())
.addMenu(new MenuBuilder(OPEN_ON) .addMenu(MenuManager.getOpenMenu(file), this.relevancePropertyProperty())
.addMenuItem(EXPLORER, e -> FileUtil.openExplorer(file))
.addMenuItem(TERMINAL, e -> FileUtil.openTerminal(file.getParentFile()))
.build(), this.relevancePropertyProperty())
.addSeparatorMenuItem() .addSeparatorMenuItem()
.addCheckMenuItem(FIXED_TAB, .addCheckMenuItem(FIXED_TAB,
e -> centerTabPaneManager.updateTabPinnedState(this)) e -> centerTabPaneManager.updateTabPinnedState(this))
@ -197,6 +183,7 @@ public class CenterTab extends Tab {
.build()); .build());
} }
/** /**
* 保存选中的文件标签页 * 保存选中的文件标签页
*/ */

View File

@ -41,5 +41,5 @@ CLOSE_OTHER_TABS=关闭其他标签页
FIXED_TAB=固定标签页 FIXED_TAB=固定标签页
READ_ONLY=只读 READ_ONLY=只读
OPEN_ON=打开于 OPEN_ON=打开于
FOLDER=文件夹

View File

@ -41,3 +41,4 @@ CLOSE_OTHER_TABS=Close Other Tabs
FIXED_TAB=Fixed Tab FIXED_TAB=Fixed Tab
READ_ONLY=Read Only READ_ONLY=Read Only
OPEN_ON=Open On OPEN_ON=Open On
FOLDER=Folder

View File

@ -41,3 +41,4 @@ CLOSE_OTHER_TABS=关闭其他标签页
FIXED_TAB=固定标签页 FIXED_TAB=固定标签页
READ_ONLY=只读 READ_ONLY=只读
OPEN_ON=打开于 OPEN_ON=打开于
FOLDER=文件夹