Răsfoiți Sursa

FileBrowser: add favorites

LeoVgr 3 săptămâni în urmă
părinte
comite
24b37c6126

+ 0 - 2
bin/res/icons/svg/generate.hx

@@ -1,2 +0,0 @@
-
-

+ 0 - 0
bin/res/icons/svg/icons.css


+ 0 - 0
bin/res/icons/svg/icons.less


+ 37 - 1
bin/style.css

@@ -4893,10 +4893,38 @@ file-browser.vertical {
 file-browser.single > .left {
 file-browser.single > .left {
   flex: 1 1;
   flex: 1 1;
 }
 }
+file-browser fancy-scroll {
+  display: block;
+  width: 100%;
+  flex: 1 1;
+  min-width: 100px;
+  overflow-x: clip;
+  overflow-y: auto;
+}
+file-browser fancy-scroll fancy-item-container {
+  position: relative;
+  display: block;
+  overflow: hidden;
+  contain: strict;
+}
 file-browser .left {
 file-browser .left {
   width: 300px;
   width: 300px;
   min-width: 100px;
   min-width: 100px;
-  background-color: mediumaquamarine;
+  background-color: #222222;
+  display: flex;
+  flex-direction: column;
+}
+file-browser .left .top {
+  width: 100%;
+  padding-bottom: 20px;
+}
+file-browser .left .top.hidden {
+  height: 0px;
+  padding-bottom: 0px;
+}
+file-browser .left .bot {
+  width: 100%;
+  flex: 1;
 }
 }
 file-browser .right {
 file-browser .right {
   flex: 1 1;
   flex: 1 1;
@@ -5244,6 +5272,14 @@ fancy-tooltip {
   mask-image: url("res/icons/svg/dot.svg");
   mask-image: url("res/icons/svg/dot.svg");
   color: #FE383A;
   color: #FE383A;
 }
 }
+.fancy-status-icon.fancy-status-icon-star::after {
+  width: 11px;
+  height: 11px;
+  left: -4px;
+  bottom: -1px;
+  mask-image: url("res/icons/svg/star.svg");
+  color: #fcbf07;
+}
 .lm_tabdropdown_list {
 .lm_tabdropdown_list {
   z-index: 9999 !important;
   z-index: 9999 !important;
   background-color: #111111;
   background-color: #111111;

+ 44 - 1
bin/style.less

@@ -5838,10 +5838,44 @@ file-browser {
 	height: 100%;
 	height: 100%;
 	width: 100%;
 	width: 100%;
 
 
+	fancy-scroll {
+		display: block;
+		width: 100%;
+		flex: 1 1;
+		min-width: 100px;
+		overflow-x: clip;
+		overflow-y: auto;
+
+		fancy-item-container {
+			position: relative;
+			display: block;
+			overflow: hidden;
+
+			contain: strict;
+		}
+
+	}
+
 	.left {
 	.left {
 		width: 300px;
 		width: 300px;
 		min-width: 100px;
 		min-width: 100px;
-		background-color: mediumaquamarine;
+		background-color: #222222;
+		display: flex;
+		flex-direction: column;
+
+		.top {
+			&.hidden {
+				height: 0px;
+				padding-bottom: 0px;
+			}
+			width: 100%;
+			padding-bottom: 20px;
+		}
+
+		.bot {
+			width: 100%;
+			flex: 1;
+		}
 	}
 	}
 
 
 	.right {
 	.right {
@@ -6280,6 +6314,15 @@ fancy-tooltip {
 		mask-image: url("res/icons/svg/dot.svg");
 		mask-image: url("res/icons/svg/dot.svg");
 		color: #FE383A;
 		color: #FE383A;
 	}
 	}
+
+	&.fancy-status-icon-star::after {
+		width: 11px;
+		height: 11px;
+		left: -4px;
+		bottom: -1px;
+		mask-image: url("res/icons/svg/star.svg");
+		color: #fcbf07;
+	}
 }
 }
 
 
 .lm_tabdropdown_list {
 .lm_tabdropdown_list {

+ 34 - 16
hide/comp/FancyTree.hx

@@ -68,7 +68,9 @@ typedef TreeButton<TreeItem> = {
 
 
 typedef Params =  {
 typedef Params =  {
 	?saveDisplayKey : String,
 	?saveDisplayKey : String,
-	?quickGoto : Bool
+	?quickGoto : Bool,
+	?search : Bool,
+	?customScroll : js.html.Element
 }
 }
 
 
 class FancyTree<TreeItem> extends hide.comp.Component {
 class FancyTree<TreeItem> extends hide.comp.Component {
@@ -83,6 +85,8 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 	var gotoFileCurrentMatch = "";
 	var gotoFileCurrentMatch = "";
 
 
 	var quickGoto : Bool = true;
 	var quickGoto : Bool = true;
+	var allowSearch : Bool = true;
+	var useCustomScroll : Bool = false;
 
 
 	static final gotoFileKeyMaxDelay = 0.5;
 	static final gotoFileKeyMaxDelay = 0.5;
 	static final overDragOpenDelaySec = 0.5;
 	static final overDragOpenDelaySec = 0.5;
@@ -118,6 +122,8 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 	public function new(parent: Element, ?params: Params) {
 	public function new(parent: Element, ?params: Params) {
 		saveDisplayKey = params?.saveDisplayKey;
 		saveDisplayKey = params?.saveDisplayKey;
 		quickGoto = params?.quickGoto != null ? params.quickGoto : true;
 		quickGoto = params?.quickGoto != null ? params.quickGoto : true;
+		allowSearch = params?.search;
+		useCustomScroll = params?.customScroll != null;
 
 
 		var el = new Element('
 		var el = new Element('
 			<fancy-tree tabindex="-1">
 			<fancy-tree tabindex="-1">
@@ -132,19 +138,21 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 
 
 		loadState();
 		loadState();
 
 
-		searchBarClosable = new FancyClosable(null, element.find("fancy-closable"));
+		if (allowSearch) {
+			searchBarClosable = new FancyClosable(null, element.find("fancy-closable"));
 
 
-		searchBar = new FancySearch(null, element.find("fancy-search"));
-		searchBar.onSearch = (search, _) -> {
-			currentSearch = search.toLowerCase();
-			queueRefresh(Search);
-		}
+			searchBar = new FancySearch(null, element.find("fancy-search"));
+			searchBar.onSearch = (search, _) -> {
+				currentSearch = search.toLowerCase();
+				queueRefresh(Search);
+			}
 
 
-		searchBarClosable.onClose = () -> {
-			onSearchClose();
+			searchBarClosable.onClose = () -> {
+				onSearchClose();
+			}
 		}
 		}
 
 
-		scroll = el.find("fancy-scroll").get(0);
+		scroll = params.customScroll == null ? el.find("fancy-scroll").get(0) : params.customScroll;
 		itemContainer = el.find("fancy-item-container").get(0);
 		itemContainer = el.find("fancy-item-container").get(0);
 		lastHeight = null;
 		lastHeight = null;
 
 
@@ -221,6 +229,12 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 		return [];
 		return [];
 	}
 	}
 
 
+	public dynamic function onSearch() {
+	}
+
+	public dynamic function onSearchEnd() {
+	}
+
 	/**
 	/**
 		Drag and drop interface.
 		Drag and drop interface.
 		Set this struct with all of it's function callback to handle drag and drop inside your tree.
 		Set this struct with all of it's function callback to handle drag and drop inside your tree.
@@ -442,10 +456,14 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 	}
 	}
 
 
 	function inputHandler(e: js.html.KeyboardEvent) {
 	function inputHandler(e: js.html.KeyboardEvent) {
+		if (!allowSearch)
+			return;
+
 		if (hide.ui.Keys.matchJsEvent("search", e, ide.currentConfig)) {
 		if (hide.ui.Keys.matchJsEvent("search", e, ide.currentConfig)) {
 			e.stopPropagation();
 			e.stopPropagation();
 			e.preventDefault();
 			e.preventDefault();
 
 
+			onSearch();
 			searchBarClosable.toggleOpen(true);
 			searchBarClosable.toggleOpen(true);
 			searchBar.focus();
 			searchBar.focus();
 			currentSearch = searchBar.getValue().toLowerCase();
 			currentSearch = searchBar.getValue().toLowerCase();
@@ -467,6 +485,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 				e.stopPropagation();
 				e.stopPropagation();
 				e.preventDefault();
 				e.preventDefault();
 
 
+				onSearchEnd();
 				searchBarClosable.toggleOpen(false);
 				searchBarClosable.toggleOpen(false);
 				searchBar.blur();
 				searchBar.blur();
 
 
@@ -525,7 +544,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 			return;
 			return;
 		}
 		}
 
 
-		if (currentItem == null || searchBar.hasFocus())
+		if (currentItem == null || searchBar?.hasFocus())
 			return;
 			return;
 
 
 		if (e.key == "ArrowRight" && hasChildren(currentItem.item)) {
 		if (e.key == "ArrowRight" && hasChildren(currentItem.item)) {
@@ -628,7 +647,8 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 			regenerateFlatData();
 			regenerateFlatData();
 		}
 		}
 
 
-		//itemContainer.innerHTML = "";
+		var scrollHeight = scroll.getBoundingClientRect().height;
+		var offsetTop = useCustomScroll ? scroll.getElementsByClassName("top")[0].getBoundingClientRect().height : 0;
 		var oldChildren = [for (node in itemContainer.childNodes) node];
 		var oldChildren = [for (node in itemContainer.childNodes) node];
 
 
 		var itemHeightPx = 20;
 		var itemHeightPx = 20;
@@ -639,13 +659,11 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 			lastHeight = height;
 			lastHeight = height;
 		}
 		}
 
 
-		var scrollHeight = scroll.getBoundingClientRect().height;
-
 		if (currentRefreshFlags.has(FocusCurrent)) {
 		if (currentRefreshFlags.has(FocusCurrent)) {
 			var currentIndex = flatData.indexOf(currentItem);
 			var currentIndex = flatData.indexOf(currentItem);
 
 
 			if (currentIndex >= 0) {
 			if (currentIndex >= 0) {
-				var currentHeight = currentIndex * itemHeightPx;
+				var currentHeight = currentIndex * itemHeightPx - offsetTop;
 				if (currentHeight < scroll.scrollTop) {
 				if (currentHeight < scroll.scrollTop) {
 					scroll.scrollTo(scroll.scrollLeft, currentHeight);
 					scroll.scrollTo(scroll.scrollLeft, currentHeight);
 				}
 				}
@@ -656,7 +674,7 @@ class FancyTree<TreeItem> extends hide.comp.Component {
 			}
 			}
 		}
 		}
 
 
-		var clipStart = scroll.scrollTop;
+		var clipStart = scroll.scrollTop - offsetTop;
 		var clipEnd = scrollHeight + clipStart;
 		var clipEnd = scrollHeight + clipStart;
 		var itemStart = hxd.Math.floor(clipStart / itemHeightPx);
 		var itemStart = hxd.Math.floor(clipStart / itemHeightPx);
 		var itemEnd = hxd.Math.ceil(clipEnd / itemHeightPx);
 		var itemEnd = hxd.Math.ceil(clipEnd / itemHeightPx);

+ 1 - 1
hide/comp/SceneEditor.hx

@@ -2065,7 +2065,7 @@ class SceneEditor {
 			icons.set(f, Reflect.field(iconsConfig,f));
 			icons.set(f, Reflect.field(iconsConfig,f));
 
 
 		var saveDisplayKey = isSceneTree ? view.saveDisplayKey + '/tree' : view.saveDisplayKey + '/renderPropsTree';
 		var saveDisplayKey = isSceneTree ? view.saveDisplayKey + '/tree' : view.saveDisplayKey + '/renderPropsTree';
-		var tree = new FancyTree<hrt.prefab.Prefab>(null, { saveDisplayKey: saveDisplayKey, quickGoto: false });
+		var tree = new FancyTree<hrt.prefab.Prefab>(null, { saveDisplayKey: saveDisplayKey, quickGoto: false, search: true });
 		tree.getChildren = (p : hrt.prefab.Prefab) -> {
 		tree.getChildren = (p : hrt.prefab.Prefab) -> {
 			if (p == null) {
 			if (p == null) {
 				if (isSceneTree)
 				if (isSceneTree)

+ 226 - 3
hide/view/FileBrowser.hx

@@ -2,6 +2,13 @@ package hide.view;
 
 
 import hide.tools.FileManager;
 import hide.tools.FileManager;
 import hide.tools.FileManager.FileEntry;
 import hide.tools.FileManager.FileEntry;
+
+typedef FavoriteEntry = {
+	parent : FavoriteEntry,
+	children : Array<FavoriteEntry>,
+	?ref : FileEntry,
+}
+
 typedef FileBrowserState = {
 typedef FileBrowserState = {
 	savedLayout: Layout,
 	savedLayout: Layout,
 }
 }
@@ -14,6 +21,7 @@ enum abstract Layout(String) {
 }
 }
 
 
 class FileBrowser extends hide.ui.View<FileBrowserState> {
 class FileBrowser extends hide.ui.View<FileBrowserState> {
+	static var FAVORITES_KEY = "filebrowser_favorites";
 
 
 	var fileTree: Element;
 	var fileTree: Element;
 	var fileIcons: Element;
 	var fileIcons: Element;
@@ -46,6 +54,10 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 	override function new(state) {
 	override function new(state) {
 		super(state);
 		super(state);
 		saveDisplayKey = "fileBrowser";
 		saveDisplayKey = "fileBrowser";
+
+		this.favorites = getDisplayState(FAVORITES_KEY);
+		if (this.favorites == null)
+			this.favorites = [];
 	}
 	}
 
 
 	override function buildTabMenu():Array<hide.comp.ContextMenu.MenuItem> {
 	override function buildTabMenu():Array<hide.comp.ContextMenu.MenuItem> {
@@ -134,6 +146,9 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 	var statFileCount: Int = 0;
 	var statFileCount: Int = 0;
 	var statFileFiltered: Int = 0;
 	var statFileFiltered: Int = 0;
 
 
+	var favorites : Array<String>;
+	var favoritesTree : hide.comp.FancyTree<FavoriteEntry>;
+
 	function set_filterEnabled(v : Bool) {
 	function set_filterEnabled(v : Bool) {
 		var anySet = false;
 		var anySet = false;
 		for (key => value in filterState) {
 		for (key => value in filterState) {
@@ -355,7 +370,12 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 
 
 		var browserLayout = new Element('
 		var browserLayout = new Element('
 			<file-browser>
 			<file-browser>
-				<div class="left"></div>
+				<div class="left">
+					<fancy-scroll>
+						<div class="top"></div>
+						<div class="bot"></div>
+					</fancy-scroll>
+				</div>
 				<div class="right" tabindex="-1">
 				<div class="right" tabindex="-1">
 					<fancy-toolbar class="fancy-small shadow">
 					<fancy-toolbar class="fancy-small shadow">
 						<fancy-button class="btn-parent quiet" title="Go to parent folder">
 						<fancy-button class="btn-parent quiet" title="Go to parent folder">
@@ -413,7 +433,182 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 			}
 			}
 		}
 		}
 
 
-		fancyTree = new hide.comp.FancyTree<FileEntry>(browserLayout.find(".left"), { saveDisplayKey: "fileBrowserTree" } );
+		// Favorites tree
+		favoritesTree = new hide.comp.FancyTree<FavoriteEntry>(browserLayout.find(".left").find(".top"), { saveDisplayKey: "fileBrowserTree_Favorites" } );
+		favoritesTree.getChildren = (file: FavoriteEntry) -> {
+			function rec(parent : FavoriteEntry) {
+				if (parent.ref.children == null)
+					return;
+				for (c in parent.ref.children) {
+					var f = { parent : parent, children : [], ref: FileManager.inst.getFileEntry(c.path) };
+					parent.children.push(f);
+					rec(f);
+				}
+			}
+
+			if (file == null) {
+				var fav : FavoriteEntry = {
+					parent : null,
+					children : [],
+					ref : null
+				}
+
+				fav.children = [];
+				for (f in favorites) {
+					// Ref could be null if this f is a favorite of another project
+					var ref = FileManager.inst.getFileEntry(f);
+					if (ref == null)
+						continue;
+					var c = { parent: fav, children: [], ref: ref }
+					fav.children.push(c);
+					rec(c);
+				}
+
+				return [fav];
+			}
+
+			if (file?.ref?.kind == File)
+				return null;
+			if (file.children == null)
+				throw "null children";
+
+			return file.children;
+
+			// return [for (c in file.children) { parent: file, children: [], ref: FileManager.inst.getFileEntry(c.ref.path) }];
+		};
+		favoritesTree.getName = (file: FavoriteEntry) -> return file.ref == null ? "Favorites" : file?.ref.name;
+		favoritesTree.getUniqueName = (file: FavoriteEntry) -> {
+			if (file == null)
+				return "";
+			if (file.ref == null)
+				return "favorites";
+			var relPath = file.ref.name;
+			var p = file.parent;
+			while (p != null) {
+				var name = p.ref == null ? "favorites" : p.ref.name;
+				relPath = name + "/" + relPath;
+				p = p.parent;
+			}
+			return relPath;
+		}
+		favoritesTree.getIcon = (file: FavoriteEntry) -> {
+			if (file.parent == null)
+				return '<div class="ico ico-star"></div>';
+
+			var fav = file.parent.ref == null ? "fancy-status-icon fancy-status-icon-star" : "";
+			if (file.ref.kind == Dir)
+				return '<div class="ico ico-folder $fav"></div>';
+			var ext = Extension.getExtension(file.ref.name);
+			if (ext != null) {
+				if (ext?.options.icon != null) {
+					return '<div class="ico ico-${ext.options.icon} $fav" title="${ext.options.name ?? "Unknown"}"></div>';
+				}
+			}
+			return '<div class="ico ico-file $fav" title="Unknown"></div>';
+		};
+		favoritesTree.onContextMenu = (item: FavoriteEntry, event: js.html.MouseEvent) -> {
+			event.stopPropagation();
+			event.preventDefault();
+
+			var options : Array<hide.comp.ContextMenu.MenuItem> = [];
+			options.push({
+				label: "Collapse",
+				click: () -> {
+					var collapseTarget = item;
+					if (item.ref.kind != Dir)
+						collapseTarget = item.parent;
+					favoritesTree.collapseItem(collapseTarget);
+				}
+			});
+			options.push({
+				label: "Collapse All",
+				click: () -> {
+					for (child in item.children)
+						favoritesTree.collapseItem(child);
+				}
+			});
+			options.push({
+				isSeparator: true
+			});
+
+			// Root favorite tree options
+			var isFavoriteRoot = item?.parent == null;
+			if (isFavoriteRoot) {
+				options.push({
+					label: "Clear Favorites",
+					click: () -> {
+						favorites = [];
+						saveDisplayState(FAVORITES_KEY, favorites);
+						favoritesTree.rebuildTree();
+						this.favoritesTree.element.parent().hide();
+					}
+				});
+
+				hide.comp.ContextMenu.createFromEvent(event, options);
+				return;
+			}
+			else {
+				if (!this.favorites.contains(item.ref.getPath())) {
+				options.push({ label: "Mark as Favorite", click : function() {
+					this.favorites.push(item.ref.getPath());
+					saveDisplayState(FAVORITES_KEY, this.favorites);
+					this.favoritesTree.rebuildTree();
+					this.favoritesTree.element.parent().show();
+				}});
+				}
+				else {
+					options.push({ label: "Remove from Favorite", click : function() {
+						this.favorites.remove(item.ref.getPath());
+						saveDisplayState(FAVORITES_KEY, this.favorites);
+						this.favoritesTree.rebuildTree();
+						if (this.favorites.length == 0)
+							this.favoritesTree.element.parent().hide();
+					}});
+				}
+			}
+
+			hide.comp.ContextMenu.createFromEvent(event, options);
+		};
+		favoritesTree.onDoubleClick = (item: FavoriteEntry) -> {
+			if (item?.ref?.kind == File)
+				ide.openFile(item.ref.getPath());
+			else
+				favoritesTree.openItem(item);
+		}
+		favoritesTree.onSelectionChanged = (enterKey) -> {
+			fancyTree.clearSelection();
+
+			var selection = favoritesTree.getSelectedItems();
+
+			// Sinc folder view with other filebrowser in SingleMiniature mode
+			if (selection.length > 0) {
+				if (selection[0].ref == null) return;
+
+				openDir(selection[0].ref, false);
+				var views = ide.getViews(hide.view.FileBrowser);
+				for (view in views) {
+					if (view == this)
+						continue;
+					if (view.layout == SingleMiniature) {
+						view.openDir(selection[0].ref, false);
+					}
+				}
+			}
+
+			if (enterKey) {
+				if (selection[0].ref.kind == File) {
+					ide.openFile(selection[0].ref.getPath());
+				}
+			}
+		}
+
+		favoritesTree.rebuildTree();
+
+		if (this.favorites.length == 0)
+			this.favoritesTree.element.parent().hide();
+			
+		// Ressources tree
+		fancyTree = new hide.comp.FancyTree<FileEntry>(browserLayout.find(".left").find(".bot"), { saveDisplayKey: "fileBrowserTree_Main", search: true, customScroll: element.find("fancy-scroll").get(0) } );
 		fancyTree.getChildren = (file: FileEntry) -> {
 		fancyTree.getChildren = (file: FileEntry) -> {
 			if (file == null)
 			if (file == null)
 				return [root];
 				return [root];
@@ -437,6 +632,15 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 
 
 		fancyTree.onNameChange = renameHandler;
 		fancyTree.onNameChange = renameHandler;
 
 
+		fancyTree.onSearch = () -> {
+			favoritesTree.element.parent().hide();
+		}
+
+		fancyTree.onSearchEnd = () -> {
+			if (this.favorites.length > 0)
+				favoritesTree.element.parent().show();
+		}
+
 		fancyTree.dragAndDropInterface =
 		fancyTree.dragAndDropInterface =
 		{
 		{
 			onDragStart: function(file: FileEntry, e: hide.tools.DragAndDrop.DragData) : Bool {
 			onDragStart: function(file: FileEntry, e: hide.tools.DragAndDrop.DragData) : Bool {
@@ -526,7 +730,6 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 		fancyGallery.getThumbnail = (item : FileEntry) -> {
 		fancyGallery.getThumbnail = (item : FileEntry) -> {
 			if (item.kind == Dir) {
 			if (item.kind == Dir) {
 				return '<fancy-image style="background-image:url(\'res/icons/svg/big_folder.svg\')"></fancy-image>';
 				return '<fancy-image style="background-image:url(\'res/icons/svg/big_folder.svg\')"></fancy-image>';
-
 			}
 			}
 			else if (item.iconPath == "loading") {
 			else if (item.iconPath == "loading") {
 				return '<fancy-image class="loading" style="background-image:url(\'res/icons/loading.gif\')"></fancy-image>';
 				return '<fancy-image class="loading" style="background-image:url(\'res/icons/loading.gif\')"></fancy-image>';
@@ -585,6 +788,8 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 
 
 
 
 		fancyTree.onSelectionChanged = (enterKey) -> {
 		fancyTree.onSelectionChanged = (enterKey) -> {
+			favoritesTree.clearSelection();
+
 			var selection = fancyTree.getSelectedItems();
 			var selection = fancyTree.getSelectedItems();
 
 
 			// Sinc folder view with other filebrowser in SingleMiniature mode
 			// Sinc folder view with other filebrowser in SingleMiniature mode
@@ -997,6 +1202,24 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 			}});
 			}});
 		}
 		}
 
 
+		if (!this.favorites.contains(item.getPath())) {
+			options.push({ label: "Mark as Favorite", click : function() {
+				this.favorites.push(item.getPath());
+				saveDisplayState(FAVORITES_KEY, this.favorites);
+				this.favoritesTree.rebuildTree();
+				this.favoritesTree.element.parent().show();
+			}});
+		}
+		else {
+			options.push({ label: "Remove from Favorite", click : function() {
+				this.favorites.remove(item.getPath());
+				saveDisplayState(FAVORITES_KEY, this.favorites);
+				this.favoritesTree.rebuildTree();
+				if (this.favorites.length == 0)
+					this.favoritesTree.element.parent().hide();
+			}});
+		}
+
 		options.push({ label: "Refresh Thumbnail(s)", click : function() {
 		options.push({ label: "Refresh Thumbnail(s)", click : function() {
 			var files = FileManager.inst.getRoots(getItemAndSelection(item, isGallery));
 			var files = FileManager.inst.getRoots(getItemAndSelection(item, isGallery));
 			for (file in files) {
 			for (file in files) {