import elements.BaseGuiElement; import elements.GuiText; import elements.GuiSkinElement; import elements.GuiButton; import elements.GuiSprite; import icons; class FileFolder : BaseGuiElement { array folders; array files; bool expanded = true; string fullPath; string basePath; string basename; GuiSkinElement@ heading; GuiButton@ btn; GuiSprite@ toggle; FileFolder(IGuiElement@ parent, const recti& pos) { super(parent, pos); @heading = GuiSkinElement(this, Alignment(Left, Top, Right, Top+30), SS_PlainBox); @btn = GuiButton(heading, Alignment().fill()); btn.style = SS_IconButton; btn.buttonIcon = spritesheet::FileIcons+2; btn.horizAlign = 0.0; btn.font = FT_Bold; @toggle = GuiSprite(btn, Alignment(Right-26, Top+4, Right-4, Top+26)); toggle.desc = icons::Minus; } void reset() { for(uint i = 0, cnt = folders.length; i < cnt; ++i) folders[i].remove(); folders.length = 0; for(uint i = 0, cnt = files.length; i < cnt; ++i) files[i].remove(); files.length = 0; } void update() { if(expanded) { toggle.desc = icons::Minus; } else { toggle.desc = icons::Plus; } if(cast(parent) !is null) cast(parent).update(); else updateAbsolutePosition(); } void clearSelection(bool upwards = true) { if(upwards) { if(cast(parent) !is null) cast(parent).clearSelection(); else clearSelection(false); } else { for(uint i = 0, cnt = folders.length; i < cnt; ++i) folders[i].clearSelection(false); for(uint i = 0, cnt = files.length; i < cnt; ++i) files[i].pressed = false; } } FileElement@ getSelection() { for(uint i = 0, cnt = files.length; i < cnt; ++i) if(files[i].pressed) return files[i]; for(uint i = 0, cnt = folders.length; i < cnt; ++i) { auto@ sel = folders[i].getSelection(); if(sel !is null) return sel; } return null; } FileElement@ getFile(const string& basePath) { for(uint i = 0, cnt = files.length; i < cnt; ++i) { if(files[i].basePath == basePath) return files[i]; } for(uint i = 0, cnt = folders.length; i < cnt; ++i) { auto@ sel = folders[i].getFile(basePath); if(sel !is null) return sel; } return null; } void select(const string& basePath, bool bubble = true) { clearSelection(); auto file = getFile(basePath); if(file !is null) { file.pressed = true; if(bubble) emitConfirmed(); } } FileFolder@ getRoot() { if(cast(parent) !is null) return cast(parent).getRoot(); else return this; } bool onGuiEvent(const GuiEvent& evt) { if(evt.type == GUI_Clicked && evt.caller is btn) { expanded = !expanded; update(); } if(evt.type == GUI_Clicked) { auto@ file = cast(evt.caller); if(file !is null) { clearSelection(); file.pressed = true; setGuiFocus(null); getRoot().emitConfirmed(); return true; } } return BaseGuiElement::onGuiEvent(evt); } void load(const string& folder, const string& base = "", const string& extension = "", bool resolve = true) { reset(); fullPath = folder; basePath = folder; if(base.length != 0) basePath = base; basename = getBasename(folder); btn.text = basename; heading.visible = false; FileList list(folder, "*", false, resolve); for(uint i = 0, cnt = list.length; i < cnt; ++i) { if(list.isDirectory[i]) { FileFolder elem(this, recti()); elem.load(path_join(fullPath, list.basename[i]), path_join(basePath, list.basename[i]), resolve=resolve, extension=extension); elem.heading.visible = true; if(elem.folders.length == 0 && elem.files.length == 0) elem.remove(); else folders.insertLast(elem); } else { string basename = list.basename[i]; if(extension.length != 0 && !basename.endswith(extension)) continue; FileElement elem(this, recti()); elem.fullPath = list.path[i]; elem.basePath = path_join(basePath, basename); elem.basename = basename; elem.update(); files.insertLast(elem); } } updateAbsolutePosition(); } uint get_needHeight() { int h = heading !is null && heading.visible ? 30 : 0; if(!expanded) return h; for(uint i = 0, cnt = folders.length; i < cnt; ++i) h += folders[i].needHeight; for(uint i = 0, cnt = files.length; i < cnt; ++i) h += files[i].needHeight; return h; } void updateAbsolutePosition() { int fileOffset = 30; int folderOffset = 0; if(cast(parent) is null) { size = vec2i(parent.size.width-20, needHeight); fileOffset = 0; folderOffset = -30; } int y = heading !is null && heading.visible ? 30 : 0; for(uint i = 0, cnt = folders.length; i < cnt; ++i) { auto@ elem = folders[i]; int h = elem.needHeight; elem.size = vec2i(size.width-30-folderOffset, h); elem.position = vec2i(30+folderOffset, y); y += h; } for(uint i = 0, cnt = files.length; i < cnt; ++i) { auto@ elem = files[i]; int h = elem.needHeight; elem.size = vec2i(size.width-fileOffset, h); elem.position = vec2i(fileOffset, y); y += h; } BaseGuiElement::updateAbsolutePosition(); } void draw() { skin.draw(SS_Panel, SF_Normal, AbsolutePosition); BaseGuiElement::draw(); } }; class FileElement : GuiButton { string fullPath; string basePath; string basename; FileElement(IGuiElement@ parent, const recti& pos) { super(parent, pos); style = SS_ListboxItem; toggleButton = true; horizAlign = 0.0; buttonIcon = spritesheet::FileIcons+0; } uint get_needHeight() { return 30; } void update() { text = basename; bool existsInMod = fileExists(path_join(topMod.abspath, basePath)); bool existsInParent = false; if(topMod.parent is null) { if(currentMod is topMod) { existsInParent = true; } else { existsInParent = fileExists(path_join(currentMod.abspath, basePath)); } } else { existsInParent = fileExists(path_join(topMod.parent.abspath, basePath)); } Color color; if(topMod is currentMod) { color = Color(0xffffffff); } else if(existsInMod) { if(existsInParent) color = Color(0xffff00ff); else color = Color(0x00ff00ff); } else { color = Color(0xaaaaaaff); } textColor = color; Icon.color = color; } };