配方库管理

GuiSearch 是玩家自己的配方库浏览器:按组浏览全部配方、重命名组、 导入导出、删除。配方本身的存在形式就是「一个标签」,所以这个界面看起来像 一个物品/配方清单。

基本信息

属性 值
实现类 me.towdium.jecalculation.gui.guis.GuiSearch extends Gui
核心映射 IdentityHashMap<ILabel, Trio<Recipe, String, Integer>> recipes(按对象同一性索引)
列表控件 WLabelScroll labels = new WLabelScroll(7, 51, 8, 6, false)
搜索控件 WSearch search = new WSearch(26, 25, 61, labels)
打开方式 合成计算器界面的按钮 → JecaGui.displayGui(new GuiSearch())
视图 6 列 × 8 行的标签网格

视图模型:下标 0 是「全部」

refreshGroups() 构造一个组切换器,第 0 项是本地化的「全部」:

ArrayList<String> groups = new ArrayList<>();
groups.add(Utilities.I18n.get("gui.search.all"));   // ← 下标 0 = "All"
groups.addAll(Controller.getGroups());
group = new WSwitcher(7, 7, 162, groups).setListener(i -> refreshDisplay());

下标越界会被夹到最后一档(if (index >= groups.size()) index = groups.size() - 1;)。

generate() 按当前下标决定数据源:

下标 数据源
0(全部) Controller.recipeIterator()
≥ 1 Controller.recipeIterator(group.getText())

列表条目是 Recipe.getRep()——即该配方的代表产物标签(取 output.get(0)):

ILabel id = i.one.getRep();
recipes.put(id, i);
identifiers.add(id);

⚠️ recipes 用的是 IdentityHashMap,即按引用相等而非 equals 索引。 由于 getRep() 返回的是 output 列表里的同一个对象引用,指向同一 ILabel 实例的条目会被覆盖——这在两条不同配方共享同一个 output.get(0) 引用时 会导致列表里出现重复标签但浮层只指得到最后一条。

「全部」视图与单组视图的按钮差异

refreshDisplay() 按下标切换按钮:

按钮 下标 0(全部) 下标 ≥ 1(单组)
导出 exportN(BTN_EXPORT_N,search.export_all)→ Controller.export() export1(BTN_EXPORT_1,search.export_group)→ Controller.export(group.getText())
删除 deleteN(BTN_DELETE_N,search.delete_all)→ 逐组遍历删除全部 delete1(BTN_DELETE_1,search.delete_group)→ 只删当前组
重命名 禁用(rename.setDisabled(group.getIndex() == 0)) 可用

「全部」视图下的删除是不可撤销的批量操作,且没有二次确认:

deleteN = new WButtonIcon(130, 25, 20, 20, BTN_DELETE_N, "search.delete_all").setListener(i -> {
    Controller.getGroups().forEach(Controller::removeGroup);
    refreshGroups();
    refreshDisplay();
});

两个导出按钮在没有配方时禁用:exportN.setDisabled(identifiers.size() == 0)。

重命名流程

点重命名进入 setRename(true):禁用组切换器,换成「输入框 + 确认 + 取消」三件套, 并移除图标/搜索/导入/导出/重命名按钮。setRename(false) 反向恢复并清空输入框。

输入框监听器控制确认按钮可用性:

yes.setDisabled(s.isEmpty() || Controller.getGroups().contains(s));

即空名或与已有组重名都不能确认。

确认后执行 Controller.renameGroup(group.getText(), text.getText()), 然后刷新组列表并把切换器文本设为新名。

onKeyPressed 里 KEY_ESCAPE 且焦点在输入框内时取消重命名并 return true (阻止 ESC 关闭整个界面)。

单条配方的浮层操作

左键点击某个标签(非 ILabel.EMPTY)弹出 Overlay:

按钮 提示键 行为
编辑 search.edit JecaGui.displayGui(true, true, new GuiRecipe(recipe.two, recipe.three)) —— 带着组名与下标打开编辑页
删除 search.delete Controller.removeRecipe(recipe.two, recipe.three),关浮层并刷新

recipe.two 是组名、recipe.three 是组内下标——这是 GuiRecipe(String group, int index) 构造器的两个参数。

标签点击的忽略条件

labels.setLsnrLeftClick((i, v) -> {
    ILabel l = i.get(v).getLabel();
    if (l != ILabel.EMPTY) setOverlay(new Overlay(i.get(v)));
});

空标签(ILabel.EMPTY)点击不弹浮层。

相关条目