import elements.BaseGuiElement; import elements.GuiListbox; import elements.GuiText; import elements.GuiTextbox; import elements.GuiButton; import dialogs.QuestionDialog; import dialogs.InputDialog; export ChooseFileMode, GuiFileChooser; enum ChooseFileMode { CFM_Filename, CFM_Single, CFM_Multiple, }; enum FileType { FT_Other, FT_Design, FT_Directory, FT_Back, }; class FileChooserElement : GuiListElement { string path; string extension; FileType type; FileChooserElement() { path = "../"; type = FT_Back; } FileChooserElement(FileList@ flist, uint num) { path = flist.basename[num]; if(flist.isDirectory[num]) { type = FT_Directory; path += "/"; } else { extension = flist.extension[num]; if(extension == "design") type = FT_Design; else type = FT_Other; } } void draw(GuiListbox@ ele, uint flags, const recti& absPos) override { const Font@ font = ele.skin.getFont(ele.TextFont); int baseLine = font.getBaseline(); vec2i textOffset(ele.horizPadding + 24, (ele.lineHeight - baseLine) / 2); if(ele.itemStyle == SS_NULL) ele.skin.draw(SS_ListboxItem, flags, absPos); spritesheet::FileIcons.draw(type, recti_area(absPos.topLeft + vec2i(ele.horizPadding, 2), vec2i(20, 20))); font.draw(absPos.topLeft + textOffset, path); } }; class FileChooserConfirm : QuestionDialogCallback { GuiFileChooser@ chooser; FileChooserConfirm(GuiFileChooser@ Chooser) { @chooser = Chooser; } void questionCallback(QuestionDialog@ dialog, int answer) { if(answer == QA_Yes) chooser.emitConfirmed(); } }; class FileChooserConfirmDelete : QuestionDialogCallback { GuiFileChooser@ chooser; FileChooserConfirmDelete(GuiFileChooser@ Chooser) { @chooser = Chooser; } void questionCallback(QuestionDialog@ dialog, int answer) { if(answer == QA_Yes) chooser.deleteFiles(); } }; class FileChooserConfirmMkdir : InputDialogCallback { GuiFileChooser@ chooser; FileChooserConfirmMkdir(GuiFileChooser@ Chooser) { @chooser = Chooser; } void inputCallback(InputDialog@ dialog, bool accepted) { if(!accepted) return; makeDirectory(chooser.realPath(dialog.getTextInput(0))); chooser.updateFiles(); } }; class GuiFileChooser : BaseGuiElement { string filter = "*"; string root; string current; ChooseFileMode mode; bool PromptOverwrite = true; GuiListbox@ list; FileList flist; GuiText@ label; GuiTextbox@ filename; GuiButton@ confirm; GuiButton@ deleteButton; GuiButton@ mkdirButton; GuiFileChooser(IGuiElement@ Parent, Alignment@ align, const string& Root, const string&in Initial, ChooseFileMode Mode, bool Management = true) { super(Parent, align); _GuiFileChooser(Root, Initial, Mode, Management); } GuiFileChooser(IGuiElement@ Parent, const recti& pos, const string&in Root, const string&in Initial, ChooseFileMode Mode, bool Management = true) { super(Parent, pos); _GuiFileChooser(Root, Initial, Mode, Management); } void _GuiFileChooser(const string& Root, const string&in Initial, ChooseFileMode Mode, bool Management) { root = Root; current = Initial; mode = Mode; if(mode == CFM_Filename) { @list = GuiListbox(this, Alignment(Left, Top+(Management ? 26 : 0), Right, Bottom-28)); @label = GuiText(this, Alignment(Left+4, Bottom-24, Left+0.25f, Bottom-2), locale::FILENAME_LABEL); @filename = GuiTextbox(this, Alignment(Left+0.25f+4, Bottom-24, Left+0.75f, Bottom-2)); @confirm = GuiButton(this, Alignment(Left+0.75f+4, Bottom-24, Right, Bottom-2), locale::SAVE); } else { @list = GuiListbox(this, Alignment(Left, Top+(Management ? 26 : 0), Right, Bottom)); if(mode == CFM_Multiple) list.multiple = true; } list.DblClickConfirm = true; list.autoMultiple = false; list.itemHeight = 24; if(Management) { @deleteButton = GuiButton(this, Alignment(Left, Top, Left+0.2f, Top+22), locale::DELETE); deleteButton.disabled = true; @mkdirButton = GuiButton(this, Alignment(Right-0.3f, Top, Right, Top+22), locale::NEW_DIRECTORY); } updateFiles(); updateAbsolutePosition(); } string realPath(string file) { return path_join(path_join(root, current), file); } void deleteFiles() { uint cnt = list.itemCount; for(uint i = 0; i < cnt; ++i) { if(!list.isSelected(i)) continue; FileChooserElement@ elem = cast(list.getItemElement(i)); if(elem is null || elem.type == FT_Back) continue; deleteFile(realPath(elem.path)); } updateFiles(); } void updateFiles() { //Navigate to new directory string dir = path_join(root, current); flist.navigate(dir, filter); //Fill file list list.clearItems(); if(current.length != 0) list.addItem(FileChooserElement()); uint cnt = flist.length; for(uint i = 0; i < cnt; ++i) if(flist.isDirectory[i]) list.addItem(FileChooserElement(flist, i)); for(uint i = 0; i < cnt; ++i) if(!flist.isDirectory[i]) list.addItem(FileChooserElement(flist, i)); } uint get_selectedCount() { return list.selectedCount; } void getSelectedFiles(array@ output, bool fullPath = true) { uint cnt = list.itemCount; for(uint i = 0; i < cnt; ++i) { if(!list.isSelected(i)) continue; FileChooserElement@ elem = cast(list.getItemElement(i)); if(elem is null) continue; if(elem.type == FT_Directory || elem.type == FT_Back) continue; if(fullPath) output.insertLast(path_join(path_join(root, current), elem.path)); else output.insertLast(path_join(current, elem.path)); } } string getSelectedPath(bool fullPath = true) { string fname; if(filename !is null) { fname = filename.text; if(fname.length == 0) return fname; } else { if(list.selectedCount == 0) return ""; fname = cast(list.selectedItem).path; } if(fullPath) return path_join(path_join(root, current), fname); else return path_join(current, fname); } string get_selectedFilename() { return filename.text; } void set_selectedFilename(string fname) { filename.text = fname; } void clickConfirm() { if(mode == CFM_Filename && PromptOverwrite) { string path = getSelectedPath(true); if(path.length != 0 && fileExists(path)) { string basepath = getSelectedPath(false); string text = format(locale::CONFIRM_OVERWRITE, basepath); question(text, locale::OVERWRITE, locale::CANCEL, FileChooserConfirm(this)); } else { emitConfirmed(); } } else { emitConfirmed(); } } void promptMkdir() { InputDialog@ dialog = InputDialog(FileChooserConfirmMkdir(this), this); dialog.accept.text = locale::CREATE_DIRECTORY; dialog.addTextInput(locale::DIRECTORY_LABEL, ""); addDialog(dialog); dialog.focusInput(); } void promptDelete() { if(selectedCount == 0) return; string text = locale::CONFIRM_DELETE_FILES+"\n"; uint cnt = list.itemCount; for(uint i = 0; i < cnt; ++i) { if(!list.isSelected(i)) continue; FileChooserElement@ elem = cast(list.getItemElement(i)); if(elem is null || elem.type == FT_Back) continue; text += "\n"+elem.path; } question(text, locale::DELETE, locale::CANCEL, FileChooserConfirmDelete(this)); } bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) { switch(event.type) { case KET_Key_Down: if(event.key == KEY_DEL) { return true; } break; case KET_Key_Up: if(event.key == KEY_DEL) { promptDelete(); return true; } break; } if(source !is list && !source.isChildOf(list)) return list.onKeyEvent(event, list); else return BaseGuiElement::onKeyEvent(event, source); } bool onGuiEvent(const GuiEvent& event) { if(event.caller is list) { switch(event.type) { case GUI_Changed: if(mode == CFM_Filename) { FileChooserElement@ elem = cast(list.selectedItem); if(elem is null || elem.type == FT_Directory || elem.type == FT_Back) return true; filename.text = elem.path; } deleteButton.disabled = list.selectedCount == 0; emitChanged(); break; case GUI_Confirmed: { FileChooserElement@ elem = cast(list.getItemElement(event.value)); if(elem is null) return true; if(elem.type == FT_Back) { if(current.length != 0) { current = path_up(current); updateFiles(); } return true; } else if(elem.type == FT_Directory) { current = path_join(current, elem.path); updateFiles(); return true; } else { clickConfirm(); return true; } } } } else if(event.type == GUI_Clicked) { if(event.caller is confirm) { clickConfirm(); return true; } else if(event.caller is deleteButton) { promptDelete(); return true; } else if(event.caller is mkdirButton) { promptMkdir(); return true; } } else if(event.caller is filename) { if(event.type == GUI_Confirmed) { clickConfirm(); return true; } } return BaseGuiElement::onGuiEvent(event); } };