Răsfoiți Sursa

CDB: Rework search bar

lviguier 6 luni în urmă
părinte
comite
2ac6938758
6 a modificat fișierele cu 344 adăugiri și 317 ștergeri
  1. 43 24
      bin/style.css
  2. 43 18
      bin/style.less
  3. 2 1
      hide/comp/IconTree.hx
  4. 227 264
      hide/comp/cdb/Editor.hx
  5. 23 4
      hide/comp/cdb/Separator.hx
  6. 6 6
      hide/view/CdbTable.hx

+ 43 - 24
bin/style.css

@@ -397,6 +397,7 @@ input[type=checkbox].indeterminate:after {
   max-width: 100%;
   max-width: 100%;
   max-height: 100%;
   max-height: 100%;
   overflow: auto;
   overflow: auto;
+  position: relative;
 }
 }
 .hide-modal {
 .hide-modal {
   position: fixed;
   position: fixed;
@@ -1049,55 +1050,73 @@ input[type=checkbox].indeterminate:after {
 .hide-tabs.tabs-bottom > .tabs-header > div.active {
 .hide-tabs.tabs-bottom > .tabs-header > div.active {
   border-radius: 0px;
   border-radius: 0px;
 }
 }
-.searchBox {
-  display: none;
-  width: 300px;
+.search-box {
   position: fixed;
   position: fixed;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  top: 45px;
+  right: 20px;
   z-index: 800;
   z-index: 800;
-  top: 20px;
-  right: 0px;
-  background-color: black;
+  background-color: #262626;
   border: 1px solid #555;
   border: 1px solid #555;
-  border-top: none;
-  border-right: none;
-  border-bottom-left-radius: 5px;
-  padding: 5px;
-  padding-bottom: 2px;
-  color: #444;
+  border-radius: 2px;
+  padding: 5px 5px 5px 5px;
+  color: #acacac;
 }
 }
-.searchBox .input-col {
-  float: left;
+.search-box .btn {
+  color: #acacac;
+  cursor: pointer;
+  margin: 0 5px 0 5px;
+  font-size: 1.2em;
+}
+.search-box .btn:hover {
+  color: #dfdfdf;
+}
+.search-box .buttons {
+  margin-right: 10px;
+}
+.search-box .search-type {
+  margin-left: 30px;
+}
+.search-box #results {
+  width: 80px;
+  margin: 0;
+  margin-left: 5px;
+  padding: 0;
+}
+.search-box .input-col {
   text-align: center;
   text-align: center;
 }
 }
-.searchBox .input-col input {
+.search-box .input-col input {
   display: block;
   display: block;
   width: 220px;
   width: 220px;
-  background-color: black;
+  background-color: #2e2e2e;
 }
 }
-.searchBox .input-col i {
+.search-box .input-col i {
   margin-top: 2px;
   margin-top: 2px;
 }
 }
-.searchBox i {
+.search-box i {
   cursor: pointer;
   cursor: pointer;
   margin-left: 5px;
   margin-left: 5px;
   display: inline-block;
   display: inline-block;
   font-size: 18px;
   font-size: 18px;
   color: #888;
   color: #888;
 }
 }
-.searchBox i:hover {
+.search-box i:hover {
   color: #EEE;
   color: #EEE;
 }
 }
-.searchBox i.hidden {
+.search-box i.hidden {
   display: none;
   display: none;
 }
 }
-.tree > .searchBox {
+.tree > .search-box {
   position: sticky;
   position: sticky;
   float: right;
   float: right;
-  top: 0px;
-  right: 0px;
+  top: 5px;
+  right: 5px;
   width: 130px;
   width: 130px;
 }
 }
-.tree > .searchBox input {
+.tree > .search-box input {
   width: 100px;
   width: 100px;
 }
 }
 /* Properties */
 /* Properties */

+ 43 - 18
bin/style.less

@@ -432,6 +432,7 @@ input[type=checkbox] {
 	max-width : 100%;
 	max-width : 100%;
 	max-height : 100%;
 	max-height : 100%;
 	overflow : auto;
 	overflow : auto;
+	position: relative;
 }
 }
 
 
 .hide-modal {
 .hide-modal {
@@ -1147,28 +1148,53 @@ input[type=checkbox] {
 	}
 	}
 }
 }
 
 
-
-.searchBox {
-	display : none;
-	width : 300px;
+.search-box {
 	position : fixed;
 	position : fixed;
+	display : flex;
+	align-items: center;
+	justify-content: center;
+	top : 45px;
+	right : 20px;
 	z-index: @popup-layer;
 	z-index: @popup-layer;
-	top : 20px;
-	right : 0px;
-	background-color : black;
+	background-color : #262626;
 	border : 1px solid #555;
 	border : 1px solid #555;
-	border-top : none;
-	border-right : none;
-	border-bottom-left-radius : 5px;
-	padding : 5px;
-	padding-bottom: 2px;
+	border-radius : 2px;
+	padding : 5px 5px 5px 5px;
+	color : #acacac;
+
+	.btn {
+		color: #acacac;
+		cursor: pointer;
+		margin: 0 5px 0 5px;
+		font-size: 1.2em;
+
+		&:hover {
+			color: #dfdfdf;
+		}
+	}
+
+	.buttons {
+		margin-right: 10px;
+	}
+
+	.search-type {
+		margin-left: 30px;
+	}
+
+	#results {
+		width: 80px;
+		margin: 0;
+		margin-left: 5px;
+		padding: 0;
+	}
+
 	.input-col {
 	.input-col {
-		float: left;
+		// float: left;
 		text-align: center;
 		text-align: center;
 		input {
 		input {
 			display: block;
 			display: block;
 			width : 220px;
 			width : 220px;
-			background-color : black;
+			background-color : #2e2e2e;
 		}
 		}
 		i {
 		i {
 			margin-top: 2px;
 			margin-top: 2px;
@@ -1187,14 +1213,13 @@ input[type=checkbox] {
 	i.hidden {
 	i.hidden {
 		display: none;
 		display: none;
 	}
 	}
-	color : #444;
 }
 }
 
 
-.tree > .searchBox {
+.tree > .search-box {
 	position: sticky;
 	position: sticky;
 	float : right;
 	float : right;
-	top : 0px;
-	right : 0px;
+	top : 5px;
+	right : 5px;
 	width : 130px;
 	width : 130px;
 	input {
 	input {
 		width : 100px;
 		width : 100px;

+ 2 - 1
hide/comp/IconTree.hx

@@ -398,7 +398,7 @@ class IconTree<T:{}> extends Component {
 			return;
 			return;
 		}
 		}
 		if( searchBox == null ) {
 		if( searchBox == null ) {
-			searchBox = new Element("<div>").addClass("searchBox").prependTo(element);
+			searchBox = new Element("<div>").addClass("search-box").prependTo(element);
 			new Element("<input type='text'>").appendTo(searchBox).keydown(function(e) {
 			new Element("<input type='text'>").appendTo(searchBox).keydown(function(e) {
 				if( e.keyCode == 27 ) {
 				if( e.keyCode == 27 ) {
 					searchBox.find("i").click();
 					searchBox.find("i").click();
@@ -419,6 +419,7 @@ class IconTree<T:{}> extends Component {
 				closeFilter();
 				closeFilter();
 			});
 			});
 		}
 		}
+		searchBox.find("input").attr("placeholder", "Find");
 		searchBox.show();
 		searchBox.show();
 		if (focus) {
 		if (focus) {
 			searchBox.find("input").focus().select();
 			searchBox.find("input").focus().select();

+ 227 - 264
hide/comp/cdb/Editor.hx

@@ -45,22 +45,14 @@ typedef EditorSheetProps = {
 	var ?categories : Array<String>;
 	var ?categories : Array<String>;
 }
 }
 
 
-typedef SearchFilter = {
-	var text: String;
-	var isExpr: Bool;
-}
-
 @:allow(hide.comp.cdb)
 @:allow(hide.comp.cdb)
 class Editor extends Component {
 class Editor extends Component {
-
 	static var COMPARISON_EXPR_CHARS = ["!=", ">=", "<=", "==", "<", ">"];
 	static var COMPARISON_EXPR_CHARS = ["!=", ">=", "<=", "==", "<", ">"];
+
 	var base : cdb.Database;
 	var base : cdb.Database;
 	var currentSheet : cdb.Sheet;
 	var currentSheet : cdb.Sheet;
 	var existsCache : Map<String,{ t : Float, r : Bool }> = new Map();
 	var existsCache : Map<String,{ t : Float, r : Bool }> = new Map();
 	var tables : Array<Table> = [];
 	var tables : Array<Table> = [];
-	var searchBox : Element;
-	var searchHidden : Bool = true;
-	var searchExp : Bool = false;
 	var pendingSearchRefresh : haxe.Timer = null;
 	var pendingSearchRefresh : haxe.Timer = null;
 	var displayMode : Table.DisplayMode;
 	var displayMode : Table.DisplayMode;
 	var clipboard : {
 	var clipboard : {
@@ -69,11 +61,16 @@ class Editor extends Component {
 		schema : Array<cdb.Data.Column>,
 		schema : Array<cdb.Data.Column>,
 	};
 	};
 	var changesDepth : Int = 0;
 	var changesDepth : Int = 0;
-	var currentFilters : Array<SearchFilter> = [];
 	var api : EditorApi;
 	var api : EditorApi;
 	var undoState : Array<UndoState> = [];
 	var undoState : Array<UndoState> = [];
 	var currentValue : Any;
 	var currentValue : Any;
 	var cdbTable : hide.view.CdbTable;
 	var cdbTable : hide.view.CdbTable;
+
+	var searchBox : Element;
+	var searchHidden : Bool = true; // Search through hidden categories
+	var searchExp : Bool = false; // Does filters are parsed by hscript parser
+	var filters : Array<String> = [];
+
 	public var view : cdb.DiffFile.ConfigView;
 	public var view : cdb.DiffFile.ConfigView;
 	public var config : hide.Config;
 	public var config : hide.Config;
 	public var cursor : Cursor;
 	public var cursor : Cursor;
@@ -216,7 +213,7 @@ class Editor extends Component {
 			e.preventDefault(); // prevent scroll
 			e.preventDefault(); // prevent scroll
 		case K.ESCAPE:
 		case K.ESCAPE:
 			if (!isRepeat) {
 			if (!isRepeat) {
-				if( currentFilters.length > 0 ) {
+				if( filters.length > 0 ) {
 					searchFilter([]);
 					searchFilter([]);
 				}
 				}
 
 
@@ -232,181 +229,157 @@ class Editor extends Component {
 	public dynamic function onScriptCtrlS() {
 	public dynamic function onScriptCtrlS() {
 	}
 	}
 
 
-	public function updateFilter() {
-		if (currentFilters.length > 0)
-			searchFilter(currentFilters, false);
-	}
 
 
-	public function setFilter( f : SearchFilter ) {
-		if( searchBox != null ) {
-			if( f == null )
-				searchBox.hide();
-			else {
-				searchBox.show();
-				searchBox.find("input").val(f.text);
-			}
-		}
-		if ( f == null )
-			searchFilter([]);
-		else
-			searchFilter([f]);
+	public function updateFilters() {
+		if (filters.length > 0)
+			searchFilter(filters, false);
 	}
 	}
 
 
-	function searchFilter( filters : Array<SearchFilter>, updateCursor=true ) {
-		while( filters.indexOf(null) >= 0 )
-			filters.remove(null);
-		for (f in filters) {
-			if (f.text == "")
-				filters.remove(f);
-		}
-		for (f in filters) {
-			if (f.text == null)
-				filters.remove(f);
-		}
-
-		function matches(haysack: String, needle: String) {
-			return haysack.indexOf(needle) >= 0;
-		}
-
+	function searchFilter( newFilters : Array<String>, updateCursor : Bool = true ) {
 		function removeAccents(str: String) {
 		function removeAccents(str: String) {
 			var t = untyped str.toLowerCase().normalize('NFD');
 			var t = untyped str.toLowerCase().normalize('NFD');
 			return ~/[\u0300-\u036f]/g.map(t, (r) -> "");
 			return ~/[\u0300-\u036f]/g.map(t, (r) -> "");
 		}
 		}
 
 
-		var all = element.find("table.cdb-sheet > tbody > tr");
-		if( config.get("cdb.filterIgnoreSublist") )
-			all = element.find("> table.cdb-sheet > tbody > tr");
-
-		all.removeClass("filtered");
-
-		if (searchExp)
-			all = all.not(".head").not(".list").not("props"); // remove potential opened list or properties
-
-		var seps = all.filter(".separator");
-		var lines = all.not(".separator");
+		// Clean new filters
+		var idx = newFilters.length;
+		while (idx >= 0) {
+			if (newFilters[idx] == null || newFilters[idx] == "")
+				newFilters.remove(newFilters[idx]);
 
 
-		var parser = new hscript.Parser();
-		parser.allowMetadata = true;
-		parser.allowTypes = true;
-		parser.allowJSON = true;
-
-		var interp = new hscript.Interp();
+			idx--;
+		}
 
 
-		var sheetNames = new Map();
-		for( s in this.base.sheets )
-			sheetNames.set(Formulas.getTypeName(s), s);
+		filters = newFilters;
 
 
-		function replaceRec( e : hscript.Expr ) {
-			switch( e.e ) {
-			case EField({ e : EIdent(s) }, name) if( sheetNames.exists(s) ):
-				if( sheetNames.get(s).idCol != null )
-					e.e = EConst(CString(name)); // replace for faster eval
-			default:
-				hscript.Tools.iter(e, replaceRec);
+		var table = tables.filter((t) -> t.sheet == currentSheet)[0];
+		if (filters.length <= 0) @:privateAccess {
+			for (l in table.lines)
+				l.element.removeClass("filtered");
+			for (s in table.separators) {
+				s.filtered = false;
+				s.refresh(false);
 			}
 			}
+			searchBox.find("#results").text('No results');
+			return;
 		}
 		}
 
 
-		if( filters.length > 0 ) {
-			var currentTable = tables.filter((t) -> t.sheet == currentSheet)[0];
-			if (searchHidden) {
-				for (l in currentTable.lines) {
-					if (l.element.hasClass("hidden"))
-						l.create();
+		var isFiltered : (line: Dynamic) -> Bool;
+		if (searchExp) {
+			var parser = new hscript.Parser();
+			parser.allowMetadata = true;
+			parser.allowTypes = true;
+			parser.allowJSON = true;
+
+			var sheetNames = new Map();
+				for( s in this.base.sheets )
+					sheetNames.set(Formulas.getTypeName(s), s);
+
+			function replaceRec( e : hscript.Expr ) {
+				switch( e.e ) {
+				case EField({ e : EIdent(s) }, name) if( sheetNames.exists(s) ):
+					if( sheetNames.get(s).idCol != null )
+						e.e = EConst(CString(name)); // replace for faster eval
+				default:
+					hscript.Tools.iter(e, replaceRec);
 				}
 				}
 			}
 			}
 
 
-			// There is two types of search :
-			// Expression search : hscript parser on search expression
-			// Litteral search : litteral text search
-			if (searchExp) {
-				this.formulas.evaluateAll(this.currentSheet.realSheet);
-
-				for (idx => l in currentSheet.lines) {
-					@:privateAccess interp.resetVariables();
-					@:privateAccess interp.initOps();
+			var interp = new hscript.Interp();
+			this.formulas.evaluateAll(this.currentSheet.realSheet);
 
 
-					interp.variables.set("Math", Math);
+			isFiltered = function(line: Dynamic) {
+				@:privateAccess interp.resetVariables();
+				@:privateAccess interp.initOps();
 
 
-					// Need deep copy here, not ideal but works
-					var cloned = haxe.Json.parse(haxe.Json.stringify(l));
-					for (f in Reflect.fields(cloned)) {
-						var c = currentTable.columns[0];
-						for (col in currentTable.columns) {
-							if (col.name == f) {
-								c = col;
-								break;
-							}
-						}
+				interp.variables.set("Math", Math);
 
 
-						switch(c.type) {
-							case cdb.Data.ColumnType.TEnum(e):
-								interp.variables.set(f, e[Reflect.getProperty(cloned, f)]);
-							default:
-								interp.variables.set(f, Reflect.getProperty(cloned, f));
+				// Need deep copy here, not ideal but works
+				var cloned = haxe.Json.parse(haxe.Json.stringify(table.sheet.lines[line.index]));
+				for (f in Reflect.fields(cloned)) {
+					var c = table.columns[0];
+					for (col in table.columns) {
+						if (col.name == f) {
+							c = col;
+							break;
 						}
 						}
 					}
 					}
 
 
-					function addFilter() {
-						var lineEl = lines.eq(idx);
-						lineEl.addClass("filtered");
-
-						var nextTr = lineEl.next('tr');
-						if (nextTr.is(".props") || nextTr.is(".list"))
-							nextTr.addClass("filtered");
+					switch(c.type) {
+						case cdb.Data.ColumnType.TEnum(e):
+							interp.variables.set(f, e[Reflect.getProperty(cloned, f)]);
+						default:
+							interp.variables.set(f, Reflect.getProperty(cloned, f));
 					}
 					}
+				}
 
 
-					// Check if the current line is filtered or not
-					var filtered = true;
-					for (f in filters) {
-						var input = f.text;
-						var expr = try parser.parseString(input) catch( e : Dynamic ) { addFilter(); continue; }
-						replaceRec(expr);
+				// function addFilter() {
+				// 	var lineEl = lines.eq(idx);
+				// 	lineEl.addClass("filtered");
 
 
-						var res = try interp.execute(expr) catch( e : hscript.Expr.Error ) { addFilter(); continue; } // Catch errors that can be thrown if search input text is not interpretabled
-						if (res) {
-							filtered = false;
-							break;
-						}
-					}
+				// 	var nextTr = lineEl.next('tr');
+				// 	if (nextTr.is(".props") || nextTr.is(".list"))
+				// 		nextTr.addClass("filtered");
+				// }
 
 
-					if (filtered)
-						addFilter();
+				// Check if the current line is filtered or not
+				for (f in filters) {
+					var expr = try parser.parseString(f) catch( e : Dynamic ) { return true; }
+					replaceRec(expr);
+
+					var res = try interp.execute(expr) catch( e : hscript.Expr.Error ) { return true; } // Catch errors that can be thrown if search input text is not interpretabled
+					if (res)
+						return false;
 				}
 				}
+
+				return true;
 			}
 			}
-			else {
-				var f_Filters = new Array<SearchFilter>();
-				for( i in 0...filters.length )
-					f_Filters.push({text:removeAccents(filters[i].text), isExpr:false});
-
-				for( t in lines ) {
-					var content = removeAccents(t.textContent);
-					if( !f_Filters.any(f -> matches(content, f.text.toLowerCase())) )
-						t.classList.add("filtered");
-				}
+		}
+		else {
+			isFiltered = function(line: hide.comp.cdb.Line) {
+				var content = removeAccents(line.element.get(0).textContent);
+				for (f in filters)
+					if (content.indexOf(removeAccents(f)) >= 0)
+						return false;
+
+				return true;
 			}
 			}
+		}
 
 
-			for( t in lines ) {
-				var l = new Element(t);
-				var parent: Element = l.data("parent-tr");
-				if( parent != null ) {
-					var f = parent.hasClass("filtered") && l.hasClass("filtered");
-					l.toggleClass("filtered", f);
-					parent.toggleClass("filtered", f);
-				}
+		// Create hidden lines to ensure they are take into account while searching
+		if (searchHidden) {
+			for (l in table.lines) {
+				if (l.element.hasClass("hidden"))
+					l.create();
 			}
 			}
+		}
 
 
-			all = all.not(".filtered");
-			if (!searchHidden)
-				all = all.not(".hidden");
-			for( s in seps.elements() ) {
-				var idx = all.index(s);
-				if( idx == all.length - 1 || new Element(all.get(idx+1)).hasClass("separator") ) {
-					s.addClass("filtered");
-				}
+		for (s in @:privateAccess table.separators)
+			@:privateAccess s.filtered = true;
+
+		var results = 0;
+		for (l in table.lines) {
+			var filtered = isFiltered(l);
+			l.element.toggleClass("filtered", filtered);
+			if (!filtered) {
+				results++;
+				var seps = Separator.getParentSeparators(l.index, @:privateAccess table.separators);
+				for (s in seps)
+					@:privateAccess s.filtered = false;
 			}
 			}
 		}
 		}
 
 
-		currentFilters = filters;
+		for (s in @:privateAccess table.separators)
+			s.refresh(false);
+
+		// Force show lines that are not filtered (even if their parent sep is collapsed)
+		for (l in table.lines) {
+			if (l.element.hasClass("hidden") && !l.element.hasClass("filtered"))
+				l.create();
+		}
+
+		searchBox.find("#results").text(results > 0 ? '$results Results' : 'No results');
+
 		if (updateCursor)
 		if (updateCursor)
 			cursor.update();
 			cursor.update();
 	}
 	}
@@ -868,7 +841,7 @@ class Editor extends Component {
 
 
 		endChanges();
 		endChanges();
 		refreshAll();
 		refreshAll();
-		updateFilter();
+		updateFilters();
 	}
 	}
 
 
 	public function changeObject( line : Line, column : cdb.Data.Column, value : Dynamic ) {
 	public function changeObject( line : Line, column : cdb.Data.Column, value : Dynamic ) {
@@ -1557,103 +1530,116 @@ class Editor extends Component {
 		element.empty();
 		element.empty();
 		element.addClass('cdb');
 		element.addClass('cdb');
 
 
-		var filters: Array<SearchFilter> = [];
+		formulas = new Formulas(this);
+		formulas.evaluateAll(currentSheet.realSheet);
 
 
-		searchBox = new Element('<div><div class="input-col"><div class="input-cont"/></div></div>').addClass("searchBox").appendTo(element);
-		var inputCont = searchBox.find(".input-cont");
-		var inputCol = searchBox.find(".input-col");
+		var content = new Element("<table>");
+		tables = [];
+		new Table(this, currentSheet, content, displayMode);
+		content.appendTo(element);
 
 
-		function removeSearchInput() {
-			if( filters.length > 1 ) {
-				inputCont.find("input").last().remove();
-				filters.pop();
-				searchFilter(filters.copy());
-				inputCol.find(".remove-btn").toggleClass("hidden", filters.length <= 1);
-			}
+		setState(state, hasFocus);
+
+		if( cursor.table != null ) {
+			for( t in tables )
+				if( t.sheet.getPath() == cursor.table.sheet.getPath() )
+					cursor.table = t;
+			cursor.update();
 		}
 		}
 
 
-		function addSearchInput() {
-			var index = filters.length;
-			filters.push({text:"", isExpr: false});
+		// Setup for search bar
+		searchBox = new Element('<div>
+			<div class="buttons">
+				<div class="btn add-btn ico ico-plus" title="Add filter"></div>
+				<div class="btn remove-btn ico ico-minus" title="Remove filter"></div>
+			</div>
+			<div class="input-col">
+				<div class="input-cont"/>
+					<input type="text" class="search-bar-cdb"></input>
+				</div>
+			</div>
+			<p id="results">No results</p>
+			<div class="btn search-type fa fa-font" title="Change search type"></div>
+			<div class="btn search-hidden fa fa-eye" title="Search through hidden categories"></div>
+			<div class="btn close-search ico ico-close" title="Close (Escape)"></div>
+		</div>').addClass("search-box").appendTo(element);
+		searchBox.hide();
+
+		var filters: Array<String> = [];
+		function search(e: js.jquery.Event) {
+			// Close search with escape
+			if( e.keyCode == K.ESCAPE ) {
+				searchBox.find(".close-search").click();
+				return;
+			}
 
 
-			var searchBar = new Element("<input type='text' class='search-bar-cdb'></input>").appendTo(inputCont).keydown(function(e) {
-				if( e.keyCode == 27 ) {
-					searchBox.find("i.close-search").click();
-					return;
-				} else if( e.keyCode == 9 && index == filters.length - 1) {
-					addSearchInput();
-					return;
-				}
-			}).keyup(function(e) {
-				// If user input a comaprison character, switch to expression mode for
-				// the current filter
-				for (c in Editor.COMPARISON_EXPR_CHARS) {
-					if (StringTools.contains(Element.getVal(e.getThis()), c) && !searchExp) {
-						searchExp = true;
-						var searchTypeBtn = searchBox.find(".search-type");
-						searchTypeBtn.toggleClass("fa-superscript", searchExp);
-						searchTypeBtn.toggleClass("fa-font", !searchExp);
-						updateFilter();
-						break;
-					}
+			// Change to expresion mode if we detect an expression character in the search (qol)
+			for (c in Editor.COMPARISON_EXPR_CHARS) {
+				if (StringTools.contains(Element.getVal(e.getThis()), c) && !searchExp) {
+					searchExp = true;
+					var searchTypeBtn = searchBox.find(".search-type");
+					searchTypeBtn.toggleClass("fa-superscript", searchExp);
+					searchTypeBtn.toggleClass("fa-font", !searchExp);
+					updateFilters();
+					break;
 				}
 				}
+			}
 
 
-				filters[index].text = Element.getVal(e.getThis());
-				filters[index].isExpr = e.getThis().next().hasClass("fa-superscript");
+			var index = e.getThis().parent().find('.search-bar-cdb').index(e.getThis());
+			if (filters[index] == null)
+				filters[index] = "";
 
 
-				// Slow table refresh protection
-				if (currentSheet.lines.length > 300) {
-					if (pendingSearchRefresh != null) {
-						pendingSearchRefresh.stop();
-					}
-					pendingSearchRefresh = haxe.Timer.delay(function()
-						{
-							searchFilter(filters.copy());
-							pendingSearchRefresh = null;
-						}, 500);
-				}
-				else {
-					searchFilter(filters.copy());
-				}
-			});
+			filters[index] = Element.getVal(e.getThis());
 
 
-			inputCol.find(".remove-btn").toggleClass("hidden", filters.length <= 1);
+			// Slow table refresh protection
+			if (currentSheet.lines.length > 300) {
+				if (pendingSearchRefresh != null) {
+					pendingSearchRefresh.stop();
+				}
+				pendingSearchRefresh = haxe.Timer.delay(function()
+					{
+						searchFilter(filters.copy());
+						pendingSearchRefresh = null;
+					}, 500);
+			}
+			else {
+				searchFilter(filters.copy());
+			}
 		}
 		}
 
 
-		var searchTypeButton = new Element("<i>").addClass("search-type fa fa-font").appendTo(searchBox);
-		searchTypeButton.attr("title", "Switch to litteral search or expression search");
-		searchTypeButton.toggleClass("fa-superscript", searchExp);
-		searchTypeButton.toggleClass("fa-font", !searchExp);
+		var inputs = searchBox.find(".search-bar-cdb");
+		inputs.attr("placeholder", "Find");
+		inputs.keyup(search);
 
 
-		searchTypeButton.click(function(_) {
-			searchExp = !searchExp;
-			searchTypeButton.toggleClass("fa-superscript", searchExp);
-			searchTypeButton.toggleClass("fa-font", !searchExp);
-			updateFilter();
+		var inputCont = searchBox.find(".input-cont");
+
+		searchBox.find(".add-btn").click(function(_) {
+			var newInput = new Element('<input type="text" class="search-bar-cdb"></input>');
+			newInput.attr("placeholder", "Find");
+			newInput.appendTo(searchBox.find(".input-cont"));
+			newInput.css({"margin-top": "2px"});
+			updateFilters();
+			searchBox.find(".remove-btn").show();
 		});
 		});
 
 
-		var hideButton = new Element("<i>").addClass("fa fa-eye").appendTo(searchBox);
-		hideButton.attr("title", "Search through hidden categories");
+		searchBox.find(".remove-btn").hide();
+		searchBox.find(".remove-btn").click(function(_) {
+			var searchBars = inputCont.find(".search-bar-cdb");
+			if( searchBars.length > 1 ) {
+				searchBars.last().remove();
+				filters.pop();
+				searchFilter(filters.copy());
 
 
-		hideButton.click(function(_) {
-			searchHidden = !searchHidden;
-			hideButton.toggleClass("fa-eye", searchHidden);
-			hideButton.toggleClass("fa-eye-slash", !searchHidden);
-			if (!searchHidden) {
-				var hiddenSeps = element.find("table.cdb-sheet > tbody > tr").not(".head").filter(".separator").filter(".sep-hidden").find("a.toggle");
-				hiddenSeps.click();
-				hiddenSeps.click();
+				if (filters.length <= 1)
+					searchBox.find(".remove-btn").hide();
 			}
 			}
-			updateFilter();
 		});
 		});
 
 
-		new Element("<i>").addClass("close-search ico ico-times-circle").appendTo(searchBox).click(function(_) {
+		searchBox.find(".close-search").click(function(_) {
 			searchFilter([]);
 			searchFilter([]);
 			searchBox.find(".search-bar-cdb").not(':first').remove();
 			searchBox.find(".search-bar-cdb").not(':first').remove();
 			searchBox.find(".expr-btn").not(':first').remove();
 			searchBox.find(".expr-btn").not(':first').remove();
-			currentFilters.clear();
 			filters.clear();
 			filters.clear();
-			filters.push({text: "", isExpr: false});
 			if(searchBox.find(".expr-btn").hasClass("fa-superscript"))
 			if(searchBox.find(".expr-btn").hasClass("fa-superscript"))
 				searchBox.find(".expr-btn").removeClass("fa-superscript").addClass("fa-font");
 				searchBox.find(".expr-btn").removeClass("fa-superscript").addClass("fa-font");
 			searchBox.toggle();
 			searchBox.toggle();
@@ -1662,49 +1648,26 @@ class Editor extends Component {
 			cursor.load(c);
 			cursor.load(c);
 			var hiddenSeps = element.find("table.cdb-sheet > tbody > tr").not(".head").filter(".separator").filter(".sep-hidden").find("a.toggle");
 			var hiddenSeps = element.find("table.cdb-sheet > tbody > tr").not(".head").filter(".separator").filter(".sep-hidden").find("a.toggle");
 			hiddenSeps.click();
 			hiddenSeps.click();
-			hiddenSeps.click();
 		});
 		});
 
 
-		new Element("<i>").addClass("add-btn ico ico-plus").appendTo(inputCol).click(function(_) {
-			addSearchInput();
-		});
-		new Element("<i>").addClass("remove-btn ico ico-minus").appendTo(inputCol).click(function(_) {
-			removeSearchInput();
+		searchBox.find(".search-type").click(function(_) {
+			searchExp = !searchExp;
+			searchBox.find(".search-type").toggleClass("fa-superscript", searchExp);
+			searchBox.find(".search-type").toggleClass("fa-font", !searchExp);
+			updateFilters();
 		});
 		});
-		addSearchInput();
-
-		formulas = new Formulas(this);
-		formulas.evaluateAll(currentSheet.realSheet);
-
-		var content = new Element("<table>");
-		tables = [];
-		new Table(this, currentSheet, content, displayMode);
-		content.appendTo(element);
-
-		setState(state, hasFocus);
 
 
-		if( cursor.table != null ) {
-			for( t in tables )
-				if( t.sheet.getPath() == cursor.table.sheet.getPath() )
-					cursor.table = t;
-			cursor.update();
-		}
-
-		if( currentFilters.length > 0 ) {
-			updateFilter();
-			searchBox.show();
-			for( i in filters.length...currentFilters.length )
-				addSearchInput();
-			if( filters.length <= currentFilters.length ) {
-				var inputs = inputCont.find("input");
-				#if js
-				for( i in 0...inputs.length ) {
-					var input: js.html.InputElement = cast inputs[i];
-					input.value = currentFilters[i].text;
-				}
-				#end
+		searchBox.find(".search-hidden").click(function(_) {
+			searchHidden = !searchHidden;
+			searchBox.find(".search-hidden").toggleClass("fa-eye", searchHidden);
+			searchBox.find(".search-hidden").toggleClass("fa-eye-slash", !searchHidden);
+			if (!searchHidden) {
+				var hiddenSeps = element.find("table.cdb-sheet > tbody > tr").not(".head").filter(".separator").filter(".sep-hidden").find("a.toggle");
+				hiddenSeps.click();
+				hiddenSeps.click();
 			}
 			}
-		}
+			updateFilters();
+		});
 	}
 	}
 
 
 	function quickExists(path) {
 	function quickExists(path) {

+ 23 - 4
hide/comp/cdb/Separator.hx

@@ -252,16 +252,35 @@ class Separator extends Component {
 	}
 	}
 
 
 
 
-	function get_visible() {
-		if (parent == null)
-			return true;
-		return parent.visible && parent.expanded && !filtered;
+	public static function getParentSeparators(lineIdx : Int, separators : Array<Separator> ) : Array<Separator> {
+		var res = [];
+		var idx = separators.length - 1;
+		while (idx >= 0) {
+			if (separators[idx].data.index <= lineIdx && ((idx + 1) >= separators.length || separators[idx + 1].data.index > lineIdx)) {
+				res.push(separators[idx]);
+				break;
+			}
+			idx--;
+		}
+
+		if (res.length == 0)
+			return res;
+
+		while (res[0].parent != null)
+			res.insert(0, res[0].parent);
+		return res;
 	}
 	}
 
 
 	public function getLinesVisiblity() {
 	public function getLinesVisiblity() {
 		return visible && expanded && !filtered;
 		return visible && expanded && !filtered;
 	}
 	}
 
 
+	function get_visible() {
+		if (parent == null)
+			return !filtered;
+		return parent.visible && parent.expanded && !filtered;
+	}
+
 	public function getLines() {
 	public function getLines() {
 		var sIdx = @:privateAccess table.separators.indexOf(this);
 		var sIdx = @:privateAccess table.separators.indexOf(this);
 		var startIdx = data.index;
 		var startIdx = data.index;

+ 6 - 6
hide/view/CdbTable.hx

@@ -44,11 +44,11 @@ class CdbTable extends hide.ui.View<{}> {
 			return;
 			return;
 		}
 		}
 
 
-		if (tabs.currentTab.get(0) != tabContents[index].parent().get(0)) {
-			@:privateAccess editor.currentFilters = [];
+		if (tabs.currentTab.get(0) != tabContents[index].parent().get(0))
 			tabs.currentTab = tabContents[index].parent();
 			tabs.currentTab = tabContents[index].parent();
-		}
-		editor.setFilter(null);
+
+		@:privateAccess editor.filters = [];
+		@:privateAccess editor.updateFilters();
 		var curTable = @:privateAccess editor.tables[0];
 		var curTable = @:privateAccess editor.tables[0];
 		var lastCell = null;
 		var lastCell = null;
 		for (i => part in path) {
 		for (i => part in path) {
@@ -173,9 +173,9 @@ class CdbTable extends hide.ui.View<{}> {
 			return;
 			return;
 		}
 		}
 
 
-		@:privateAccess editor.currentFilters = [];
 		tabs.currentTab = tabContents[index].parent();
 		tabs.currentTab = tabContents[index].parent();
-		editor.setFilter(null);
+		@:privateAccess editor.filters = [];
+		@:privateAccess editor.updateFilters();
 		if( line != null ) {
 		if( line != null ) {
 			if( column != null )
 			if( column != null )
 				editor.cursor.setDefault(line, column);
 				editor.cursor.setDefault(line, column);