forked from mbk-lab/rui_orig
				
			Added TableView cell/row selection mode
This commit is contained in:
		
							parent
							
								
									15a11dc558
								
							
						
					
					
						commit
						1a4040bd00
					
				|  | @ -1,3 +1,7 @@ | |||
| # v0.5.0 | ||||
| 
 | ||||
| * Added HasFocus function to the View interface | ||||
| 
 | ||||
| # v0.4.0 | ||||
| 
 | ||||
| * Added SetTitle and SetTitleColor function to the Session interface | ||||
|  |  | |||
							
								
								
									
										485
									
								
								app_scripts.js
								
								
								
								
							
							
						
						
									
										485
									
								
								app_scripts.js
								
								
								
								
							|  | @ -615,12 +615,13 @@ function selectDropDownListItem(elementId, number) { | |||
| 
 | ||||
| function listItemClickEvent(element, event) { | ||||
| 	event.stopPropagation(); | ||||
| 	 | ||||
| 	var selected = false; | ||||
| 	if (element.classList) { | ||||
| 		selected = (element.classList.contains("ruiListItemFocused") || element.classList.contains("ruiListItemSelected")); | ||||
| 	} else { | ||||
| 		selected = element.className.indexOf("ruiListItemFocused") >= 0 || element.className.indexOf("ruiListItemSelected") >= 0; | ||||
| 	} | ||||
| 		const focusStyle = getListFocusedItemStyle(element); | ||||
| 		const blurStyle = getListSelectedItemStyle(element); | ||||
| 		selected = (element.classList.contains(focusStyle) || element.classList.contains(blurStyle)); | ||||
| 	}  | ||||
| 
 | ||||
| 	var list = element.parentNode.parentNode | ||||
| 	if (list) { | ||||
|  | @ -640,18 +641,27 @@ function getListItemNumber(itemId) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| function getStyleAttribute(element, attr, defValue) { | ||||
| 	var result = element.getAttribute(attr); | ||||
| 	if (result) { | ||||
| 		return result; | ||||
| 	} | ||||
| 	return defValue; | ||||
| } | ||||
| 
 | ||||
| function getListFocusedItemStyle(element) { | ||||
| 	return getStyleAttribute(element, "data-focusitemstyle", "ruiListItemFocused"); | ||||
| } | ||||
| 
 | ||||
| function getListSelectedItemStyle(element) { | ||||
| 	return getStyleAttribute(element, "data-bluritemstyle", "ruiListItemSelected"); | ||||
| } | ||||
| 
 | ||||
| function selectListItem(element, item, needSendMessage) { | ||||
| 	var currentId = element.getAttribute("data-current"); | ||||
| 	var message; | ||||
| 	var focusStyle = element.getAttribute("data-focusitemstyle"); | ||||
| 	var blurStyle = element.getAttribute("data-bluritemstyle"); | ||||
| 
 | ||||
| 	if (!focusStyle) { | ||||
| 		focusStyle = "ruiListItemFocused" | ||||
| 	} | ||||
| 	if (!blurStyle) { | ||||
| 		blurStyle = "ruiListItemSelected" | ||||
| 	} | ||||
| 	const focusStyle = getListFocusedItemStyle(element); | ||||
| 	const blurStyle = getListSelectedItemStyle(element); | ||||
| 
 | ||||
| 	if (currentId) { | ||||
| 		var current = document.getElementById(currentId); | ||||
|  | @ -801,24 +811,29 @@ function findBottomListItem(list, x, y) { | |||
| 	return result | ||||
| } | ||||
| 
 | ||||
| function listViewKeyDownEvent(element, event) { | ||||
| 	var key; | ||||
| function getKey(event) { | ||||
| 	if (event.key) { | ||||
| 		key = event.key; | ||||
| 	} else if (event.keyCode) { | ||||
| 		return event.key; | ||||
| 	} | ||||
| 
 | ||||
| 	if (event.keyCode) { | ||||
| 		switch (event.keyCode) { | ||||
| 			case 13: key = "Enter"; break; | ||||
| 			case 32: key = " "; break; | ||||
| 			case 33: key = "PageUp"; break; | ||||
| 			case 34: key = "PageDown"; break; | ||||
| 			case 35: key = "End"; break; | ||||
| 			case 36: key = "Home"; break; | ||||
| 			case 37: key = "ArrowLeft"; break; | ||||
| 			case 38: key = "ArrowUp"; break; | ||||
| 			case 39: key = "ArrowRight"; break; | ||||
| 			case 40: key = "ArrowDown"; break; | ||||
| 			case 13: return "Enter"; | ||||
| 			case 32: return " "; | ||||
| 			case 33: return "PageUp"; | ||||
| 			case 34: return "PageDown"; | ||||
| 			case 35: return "End"; | ||||
| 			case 36: return "Home"; | ||||
| 			case 37: return "ArrowLeft"; | ||||
| 			case 38: return "ArrowUp"; | ||||
| 			case 39: return "ArrowRight"; | ||||
| 			case 40: return "ArrowDown"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function listViewKeyDownEvent(element, event) { | ||||
| 	const key = getKey(event); | ||||
| 	if (key) { | ||||
| 		var currentId = element.getAttribute("data-current"); | ||||
| 		var current | ||||
|  | @ -885,21 +900,10 @@ function listViewFocusEvent(element, event) { | |||
| 	if (currentId) { | ||||
| 		var current = document.getElementById(currentId); | ||||
| 		if (current) { | ||||
| 			var focusStyle = element.getAttribute("data-focusitemstyle"); | ||||
| 			var blurStyle = element.getAttribute("data-bluritemstyle"); | ||||
| 			if (!focusStyle) { | ||||
| 				focusStyle = "ruiListItemFocused" | ||||
| 			} | ||||
| 			if (!blurStyle) { | ||||
| 				blurStyle = "ruiListItemSelected" | ||||
| 			} | ||||
| 			 | ||||
| 			if (current.classList) { | ||||
| 				current.classList.remove(blurStyle); | ||||
| 				current.classList.add(focusStyle); | ||||
| 			} else { // IE < 10
 | ||||
| 				current.className = "ruiListItem " + focusStyle; | ||||
| 			} | ||||
| 				current.classList.remove(getListSelectedItemStyle(element)); | ||||
| 				current.classList.add(getListFocusedItemStyle(element)); | ||||
| 			}  | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -909,20 +913,9 @@ function listViewBlurEvent(element, event) { | |||
| 	if (currentId) { | ||||
| 		var current = document.getElementById(currentId); | ||||
| 		if (current) { | ||||
| 			var focusStyle = element.getAttribute("data-focusitemstyle"); | ||||
| 			var blurStyle = element.getAttribute("data-bluritemstyle"); | ||||
| 			if (!focusStyle) { | ||||
| 				focusStyle = "ruiListItemFocused" | ||||
| 			} | ||||
| 			if (!blurStyle) { | ||||
| 				blurStyle = "ruiListItemSelected" | ||||
| 			} | ||||
| 
 | ||||
| 			if (current.classList) { | ||||
| 				current.classList.remove(focusStyle); | ||||
| 				current.classList.add(blurStyle); | ||||
| 			} else { // IE < 10
 | ||||
| 				current.className = "ruiListItem " + blurStyle; | ||||
| 				current.classList.remove(getListFocusedItemStyle(element)); | ||||
| 				current.classList.add(getListSelectedItemStyle(element)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1372,4 +1365,388 @@ function setTitleColor(color) { | |||
| 
 | ||||
| function detailsEvent(element) { | ||||
| 	sendMessage("details-open{session=" + sessionID + ",id=" + element.id + ",open=" + (element.open ? "1}" : "0}")); | ||||
| } | ||||
| 
 | ||||
| function getTableFocusedItemStyle(element) { | ||||
| 	return getStyleAttribute(element, "data-focusitemstyle", "ruiCurrentTableCellFocused"); | ||||
| } | ||||
| 
 | ||||
| function getTableSelectedItemStyle(element) { | ||||
| 	return getStyleAttribute(element, "data-bluritemstyle", "ruiCurrentTableCell"); | ||||
| } | ||||
| 
 | ||||
| function tableViewFocusEvent(element, event) { | ||||
| 	var currentId = element.getAttribute("data-current"); | ||||
| 	if (currentId) { | ||||
| 		var current = document.getElementById(currentId); | ||||
| 		if (current) { | ||||
| 			if (current.classList) { | ||||
| 				current.classList.remove(getTableSelectedItemStyle(element)); | ||||
| 				current.classList.add(getTableFocusedItemStyle(element)); | ||||
| 			}  | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function tableViewBlurEvent(element, event) { | ||||
| 	var currentId = element.getAttribute("data-current"); | ||||
| 	if (currentId) { | ||||
| 		var current = document.getElementById(currentId); | ||||
| 		if (current && current.classList) { | ||||
| 			current.classList.remove(getTableFocusedItemStyle(element)); | ||||
| 			current.classList.add(getTableSelectedItemStyle(element)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function setTableCellCursor(element, row, column) { | ||||
| 	const cellID = element.id + "-" + row + "-" + column; | ||||
| 	var cell = document.getElementById(cellID); | ||||
| 	if (!cell) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	const focusStyle = getTableFocusedItemStyle(element); | ||||
| 	const oldCellID = element.getAttribute("data-current"); | ||||
| 	if (oldCellID) { | ||||
| 		const oldCell = document.getElementById(oldCellID); | ||||
| 		if (oldCell && oldCell.classList) { | ||||
| 			oldCell.classList.remove(focusStyle); | ||||
| 			oldCell.classList.remove(getTableSelectedItemStyle(element)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	cell.classList.add(focusStyle); | ||||
| 	element.setAttribute("data-current", cellID); | ||||
| 
 | ||||
| 	sendMessage("currentCell{session=" + sessionID + ",id=" + element.id +  | ||||
| 			",row=" + row + ",column=" + column + "}"); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| function moveTableCellCursor(element, row, column, dr, dc) { | ||||
| 	const rows = element.getAttribute("data-rows"); | ||||
| 	if (!rows) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const columns = element.getAttribute("data-columns"); | ||||
| 	if (!columns) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const rowCount = parseInt(rows); | ||||
| 	const columnCount = parseInt(columns); | ||||
| 	 | ||||
| 	row += dr; | ||||
| 	column += dc; | ||||
| 	while (row >= 0 && row < rowCount && column >= 0 && column < columnCount) { | ||||
| 		if (setTableCellCursor(element, row, column)) { | ||||
| 			return; | ||||
| 		} else if (dr == 0) { | ||||
| 			var r2 = row - 1; | ||||
| 			while (r2 >= 0) { | ||||
| 				if (setTableCellCursor(element, r2, column)) { | ||||
| 					return; | ||||
| 				} | ||||
| 				r2--; | ||||
| 			} | ||||
| 		} else if (dc == 0) { | ||||
| 			var c2 = column - 1; | ||||
| 			while (c2 >= 0) { | ||||
| 				if (setTableCellCursor(element, row, c2)) { | ||||
| 					return; | ||||
| 				} | ||||
| 				c2--; | ||||
| 			} | ||||
| 		} | ||||
| 		row += dr; | ||||
| 		column += dc; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function tableViewCellKeyDownEvent(element, event) { | ||||
| 	const key = getKey(event); | ||||
| 	if (key) { | ||||
| 		const currentId = element.getAttribute("data-current"); | ||||
| 		if (currentId) { | ||||
| 			const elements = currentId.split("-"); | ||||
| 			if (elements.length >= 3) { | ||||
| 				const row = parseInt(elements[1], 10) | ||||
| 				const column = parseInt(elements[2], 10) | ||||
| 
 | ||||
| 				switch (key) { | ||||
| 				case " ":  | ||||
| 				case "Enter": | ||||
| 					sendMessage("cellClick{session=" + sessionID + ",id=" + element.id +  | ||||
| 								",row=" + row + ",column=" + column + "}"); | ||||
| 					break; | ||||
| 	 | ||||
| 				case "ArrowLeft": | ||||
| 					moveTableCellCursor(element, row, column, 0, -1) | ||||
| 					break; | ||||
| 				 | ||||
| 				case "ArrowRight": | ||||
| 					moveTableCellCursor(element, row, column, 0, 1) | ||||
| 					break; | ||||
| 		 | ||||
| 				case "ArrowDown": | ||||
| 					moveTableCellCursor(element, row, column, 1, 0) | ||||
| 					break; | ||||
| 	 | ||||
| 				case "ArrowUp": | ||||
| 					moveTableCellCursor(element, row, column, -1, 0) | ||||
| 					break; | ||||
| 	 | ||||
| 				case "Home": | ||||
| 					// TODO
 | ||||
| 					break; | ||||
| 	 | ||||
| 				case "End": | ||||
| 					/*var newRow = rowCount-1; | ||||
| 					while (newRow > row) { | ||||
| 						if (setTableRowCursor(element, newRow)) { | ||||
| 							break; | ||||
| 						} | ||||
| 						newRow--; | ||||
| 					}*/ | ||||
| 					// TODO
 | ||||
| 					break; | ||||
| 	 | ||||
| 				case "PageUp": | ||||
| 					// TODO
 | ||||
| 					break; | ||||
| 	 | ||||
| 				case "PageDown": | ||||
| 					// TODO
 | ||||
| 					break; | ||||
| 	 | ||||
| 				default: | ||||
| 					return; | ||||
| 				} | ||||
| 				 | ||||
| 				event.stopPropagation(); | ||||
| 				event.preventDefault(); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		switch (key) { | ||||
| 		case "ArrowLeft": | ||||
| 		case "ArrowRight": | ||||
| 		case "ArrowDown": | ||||
| 		case "ArrowUp": | ||||
| 		case "Home": | ||||
| 		case "End": | ||||
| 		case "PageUp": | ||||
| 		case "PageDown": | ||||
| 			const rows = element.getAttribute("data-rows"); | ||||
| 			const columns = element.getAttribute("data-columns"); | ||||
| 			if (rows && columns) { | ||||
| 				const rowCount = parseInt(rows); | ||||
| 				const columnCount = parseInt(rows); | ||||
| 				row = 0; | ||||
| 				while (row < rowCount) { | ||||
| 					column = 0; | ||||
| 					while (columns < columnCount) { | ||||
| 						if (setTableCellCursor(element, row, column)) { | ||||
| 							event.stopPropagation(); | ||||
| 							event.preventDefault(); | ||||
| 							return; | ||||
| 						} | ||||
| 						column++; | ||||
| 					} | ||||
| 					row++; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| 	event.stopPropagation(); | ||||
| 	event.preventDefault(); | ||||
| } | ||||
| 
 | ||||
| function setTableRowCursor(element, row) { | ||||
| 	const tableRowID = element.id + "-" + row; | ||||
| 	var tableRow = document.getElementById(tableRowID); | ||||
| 	if (!tableRow) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	const focusStyle = getTableFocusedItemStyle(element); | ||||
| 	const oldRowID = element.getAttribute("data-current"); | ||||
| 	if (oldRowID) { | ||||
| 		const oldRow = document.getElementById(oldRowID); | ||||
| 		if (oldRow && oldRow.classList) { | ||||
| 			oldRow.classList.remove(focusStyle); | ||||
| 			oldRow.classList.remove(getTableSelectedItemStyle(element)); | ||||
| 
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	tableRow.classList.add(focusStyle); | ||||
| 	element.setAttribute("data-current", tableRowID); | ||||
| 
 | ||||
| 	sendMessage("currentRow{session=" + sessionID + ",id=" + element.id + ",row=" + row + "}"); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| function moveTableRowCursor(element, row, dr) { | ||||
| 	const rows = element.getAttribute("data-rows"); | ||||
| 	if (!rows) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const rowCount = parseInt(rows); | ||||
| 	row += dr; | ||||
| 	while (row >= 0 && row < rowCount) { | ||||
| 		if (setTableRowCursor(element, row)) { | ||||
| 			return; | ||||
| 		} | ||||
| 		row += dr; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function tableViewRowKeyDownEvent(element, event) { | ||||
| 	const key = getKey(event); | ||||
| 	if (key) { | ||||
| 		const currentId = element.getAttribute("data-current"); | ||||
| 		if (currentId) { | ||||
| 			const elements = currentId.split("-"); | ||||
| 			if (elements.length >= 2) { | ||||
| 				const row = parseInt(elements[1], 10); | ||||
| 				switch (key) { | ||||
| 					case " ":  | ||||
| 					case "Enter": | ||||
| 						sendMessage("rowClick{session=" + sessionID + ",id=" + element.id + ",row=" + row + "}"); | ||||
| 						break; | ||||
| 		 | ||||
| 					case "ArrowDown": | ||||
| 						moveTableRowCursor(element, row, 1) | ||||
| 						break; | ||||
| 		 | ||||
| 					case "ArrowUp": | ||||
| 						moveTableRowCursor(element, row, -1) | ||||
| 						break; | ||||
| 		 | ||||
| 					case "Home": | ||||
| 						var newRow = 0; | ||||
| 						while (newRow < row) { | ||||
| 							if (setTableRowCursor(element, newRow)) { | ||||
| 								break; | ||||
| 							} | ||||
| 							newRow++; | ||||
| 						} | ||||
| 						break; | ||||
| 		 | ||||
| 					case "End": | ||||
| 						var newRow = rowCount-1; | ||||
| 						while (newRow > row) { | ||||
| 							if (setTableRowCursor(element, newRow)) { | ||||
| 								break; | ||||
| 							} | ||||
| 							newRow--; | ||||
| 						} | ||||
| 						break; | ||||
| 		 | ||||
| 					case "PageUp": | ||||
| 						// TODO
 | ||||
| 						break; | ||||
| 		 | ||||
| 					case "PageDown": | ||||
| 						// TODO
 | ||||
| 						break; | ||||
| 		 | ||||
| 					default: | ||||
| 						return; | ||||
| 				} | ||||
| 				event.stopPropagation(); | ||||
| 				event.preventDefault(); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		switch (key) { | ||||
| 			case "ArrowLeft": | ||||
| 			case "ArrowRight": | ||||
| 			case "ArrowDown": | ||||
| 			case "ArrowUp": | ||||
| 			case "Home": | ||||
| 			case "End": | ||||
| 			case "PageUp": | ||||
| 			case "PageDown": | ||||
| 				const rows = element.getAttribute("data-rows"); | ||||
| 				if (rows) { | ||||
| 					const rowCount = parseInt(rows); | ||||
| 					row = 0; | ||||
| 					while (row < rowCount) { | ||||
| 						if (setTableRowCursor(element, row)) { | ||||
| 							break; | ||||
| 						} | ||||
| 						row++; | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 
 | ||||
| 			default: | ||||
| 				return; | ||||
| 		} | ||||
| 	}  | ||||
| 
 | ||||
| 	event.stopPropagation(); | ||||
| 	event.preventDefault(); | ||||
| } | ||||
| 
 | ||||
| function tableCellClickEvent(element, event) { | ||||
| 	event.preventDefault(); | ||||
| 
 | ||||
| 	const elements = element.id.split("-"); | ||||
| 	if (elements.length < 3) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	const tableID = elements[0]; | ||||
| 	const row = parseInt(elements[1], 10); | ||||
| 	const column = parseInt(elements[2], 10); | ||||
| 	const table = document.getElementById(tableID); | ||||
| 	if (table) { | ||||
| 		const selection = table.getAttribute("data-selection"); | ||||
| 		if (selection == "cell") { | ||||
| 			const currentID = table.getAttribute("data-current"); | ||||
| 			if (!currentID || currentID != element.ID) { | ||||
| 				setTableCellCursor(table, row, column) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	sendMessage("cellClick{session=" + sessionID + ",id=" + tableID +  | ||||
| 				",row=" + row + ",column=" + column + "}"); | ||||
| } | ||||
| 
 | ||||
| function tableRowClickEvent(element, event) { | ||||
| 	event.preventDefault(); | ||||
| 
 | ||||
| 	const elements = element.id.split("-"); | ||||
| 	if (elements.length < 2) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	const tableID = elements[0]; | ||||
| 	const row = parseInt(elements[1], 10); | ||||
| 	const table = document.getElementById(tableID); | ||||
| 	if (table) { | ||||
| 		const selection = table.getAttribute("data-selection"); | ||||
| 		if (selection == "cell") { | ||||
| 			const currentID = table.getAttribute("data-current"); | ||||
| 			if (!currentID || currentID != element.ID) { | ||||
| 				setTableRowCursor(table, row) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	sendMessage("rowClick{session=" + sessionID + ",id=" + tableID + ",row=" + row + "}"); | ||||
| } | ||||
|  | @ -148,11 +148,15 @@ func (customView *CustomViewData) Scroll() Frame { | |||
| 	return customView.superView.Scroll() | ||||
| } | ||||
| 
 | ||||
| func (customView *CustomViewData) HasFocus() bool { | ||||
| 	return customView.superView.HasFocus() | ||||
| } | ||||
| 
 | ||||
| func (customView *CustomViewData) onResize(self View, x, y, width, height float64) { | ||||
| 	customView.superView.onResize(customView.superView, x, y, width, height) | ||||
| } | ||||
| 
 | ||||
| func (customView *CustomViewData) onItemResize(self View, index int, x, y, width, height float64) { | ||||
| func (customView *CustomViewData) onItemResize(self View, index string, x, y, width, height float64) { | ||||
| 	customView.superView.onItemResize(customView.superView, index, x, y, width, height) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -44,6 +44,10 @@ func (picker *datePickerData) Init(session Session) { | |||
| 	picker.dateChangedListeners = []func(DatePicker, time.Time){} | ||||
| } | ||||
| 
 | ||||
| func (picker *datePickerData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (picker *datePickerData) normalizeTag(tag string) string { | ||||
| 	tag = strings.ToLower(tag) | ||||
| 	switch tag { | ||||
|  |  | |||
|  | @ -213,24 +213,32 @@ theme { | |||
| 			text-color = @ruiPopupTextColor, | ||||
| 			radius = 4px, | ||||
| 			shadow = _{spread-radius=4px, blur=16px, color=#80808080}, | ||||
| 		} | ||||
| 		}, | ||||
| 		ruiPopupTitle { | ||||
| 			background-color = @ruiPopupTitleColor, | ||||
| 			text-color = @ruiPopupTitleTextColor, | ||||
| 			min-height = 24px, | ||||
| 		} | ||||
| 		}, | ||||
| 		ruiMessageText { | ||||
| 			padding-left = 64px, | ||||
| 			padding-right = 64px, | ||||
| 			padding-top = 32px, | ||||
| 			padding-bottom = 32px, | ||||
| 		} | ||||
| 		}, | ||||
| 		ruiPopupMenuItem { | ||||
| 			padding-top = 4px, | ||||
| 			padding-bottom = 4px, | ||||
| 			padding-left = 8px, | ||||
| 			padding-right = 8px, | ||||
| 		} | ||||
| 		}, | ||||
| 		ruiCurrentTableCell { | ||||
| 			background-color=@ruiSelectedColor, | ||||
| 			text-color=@ruiSelectedTextColor, | ||||
| 		}, | ||||
| 		ruiCurrentTableCellFocused { | ||||
| 			background-color=@ruiHighlightColor, | ||||
| 			text-color=@ruiHighlightTextColor, | ||||
| 		}, | ||||
| 	], | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,8 @@ GridLayout { | |||
| 						DropDownList { row = 5, column = 1, id = tableFootStyle, current = 0, items = ["none", "tableFoot1", "rui.Params"]}, | ||||
| 						Checkbox { row = 6, column = 0:1, id = tableRowStyle, content = "Row style" }, | ||||
| 						Checkbox { row = 7, column = 0:1, id = tableColumnStyle, content = "Column style" }, | ||||
| 						TextView { row = 8, text = "Selection mode" }, | ||||
| 						DropDownList { row = 8, column = 1, id = tableSelectionMode, current = 0, items = ["none", "cell", "row"]}, | ||||
| 					] | ||||
| 				} | ||||
| 			] | ||||
|  | @ -93,6 +95,10 @@ func createTableViewDemo(session rui.Session) rui.View { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	rui.Set(view, "tableSelectionMode", rui.DropDownEvent, func(list rui.DropDownList, number int) { | ||||
| 		rui.Set(view, "demoTableView1", rui.SelectionMode, number) | ||||
| 	}) | ||||
| 
 | ||||
| 	rui.Set(view, "tableCellGap", rui.DropDownEvent, func(list rui.DropDownList, number int) { | ||||
| 		if number == 0 { | ||||
| 			rui.Set(view, "demoTableView1", rui.Gap, rui.Px(0)) | ||||
|  |  | |||
|  | @ -39,6 +39,10 @@ func (list *dropDownListData) Init(session Session) { | |||
| 	list.dropDownListener = []func(DropDownList, int){} | ||||
| } | ||||
| 
 | ||||
| func (list *dropDownListData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (list *dropDownListData) Remove(tag string) { | ||||
| 	list.remove(strings.ToLower(tag)) | ||||
| } | ||||
|  |  | |||
|  | @ -63,6 +63,10 @@ func (edit *editViewData) Init(session Session) { | |||
| 	edit.tag = "EditView" | ||||
| } | ||||
| 
 | ||||
| func (edit *editViewData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (edit *editViewData) normalizeTag(tag string) string { | ||||
| 	tag = strings.ToLower(tag) | ||||
| 	switch tag { | ||||
|  |  | |||
|  | @ -89,6 +89,10 @@ func (picker *filePickerData) Init(session Session) { | |||
| 	picker.fileSelectedListeners = []func(FilePicker, []FileInfo){} | ||||
| } | ||||
| 
 | ||||
| func (picker *filePickerData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (picker *filePickerData) Files() []FileInfo { | ||||
| 	return picker.files | ||||
| } | ||||
|  |  | |||
|  | @ -140,11 +140,9 @@ func getFocusListeners(view View, subviewID string, tag string) []func(View) { | |||
| } | ||||
| 
 | ||||
| func focusEventsHtml(view View, buffer *strings.Builder) { | ||||
| 	for tag, js := range focusEvents { | ||||
| 		if value := view.getRaw(tag); value != nil { | ||||
| 			if listeners, ok := value.([]func(View)); ok && len(listeners) > 0 { | ||||
| 				buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `) | ||||
| 			} | ||||
| 	if view.Focusable() { | ||||
| 		for _, js := range focusEvents { | ||||
| 			buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										23
									
								
								listView.go
								
								
								
								
							
							
						
						
									
										23
									
								
								listView.go
								
								
								
								
							|  | @ -1083,14 +1083,12 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| func (listView *listViewData) handleCommand(self View, command string, data DataObject) bool { | ||||
| 	switch command { | ||||
| 	case "itemSelected": | ||||
| 		if text, ok := data.PropertyValue(`number`); ok { | ||||
| 			if number, err := strconv.Atoi(text); err == nil { | ||||
| 				listView.properties[Current] = number | ||||
| 				for _, listener := range listView.selectedListeners { | ||||
| 					listener(listView, number) | ||||
| 				} | ||||
| 				listView.propertyChangedEvent(Current) | ||||
| 		if number, ok := dataIntProperty(data, `number`); ok { | ||||
| 			listView.properties[Current] = number | ||||
| 			for _, listener := range listView.selectedListeners { | ||||
| 				listener(listView, number) | ||||
| 			} | ||||
| 			listView.propertyChangedEvent(Current) | ||||
| 		} | ||||
| 
 | ||||
| 	case "itemUnselected": | ||||
|  | @ -1162,9 +1160,14 @@ func (listView *listViewData) onItemClick() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (listView *listViewData) onItemResize(self View, index int, x, y, width, height float64) { | ||||
| 	if index >= 0 && index < len(listView.itemFrame) { | ||||
| 		listView.itemFrame[index] = Frame{Left: x, Top: y, Width: width, Height: height} | ||||
| func (listView *listViewData) onItemResize(self View, index string, x, y, width, height float64) { | ||||
| 	n, err := strconv.Atoi(index) | ||||
| 	if err != nil { | ||||
| 		ErrorLog(err.Error()) | ||||
| 	} else if n >= 0 && n < len(listView.itemFrame) { | ||||
| 		listView.itemFrame[n] = Frame{Left: x, Top: y, Width: width, Height: height} | ||||
| 	} else { | ||||
| 		ErrorLogF(`Invalid ListView item index: %d`, n) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -168,6 +168,10 @@ func (player *mediaPlayerData) Init(session Session) { | |||
| 	player.tag = "MediaPlayer" | ||||
| } | ||||
| 
 | ||||
| func (player *mediaPlayerData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (player *mediaPlayerData) Remove(tag string) { | ||||
| 	player.remove(strings.ToLower(tag)) | ||||
| } | ||||
|  |  | |||
|  | @ -50,6 +50,10 @@ func (picker *numberPickerData) Init(session Session) { | |||
| 	picker.numberChangedListeners = []func(NumberPicker, float64){} | ||||
| } | ||||
| 
 | ||||
| func (picker *numberPickerData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (picker *numberPickerData) normalizeTag(tag string) string { | ||||
| 	tag = strings.ToLower(tag) | ||||
| 	switch tag { | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ func (view *viewData) onResize(self View, x, y, width, height float64) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (view *viewData) onItemResize(self View, index int, x, y, width, height float64) { | ||||
| func (view *viewData) onItemResize(self View, index string, x, y, width, height float64) { | ||||
| } | ||||
| 
 | ||||
| func (view *viewData) setFrameListener(tag string, value interface{}) bool { | ||||
|  |  | |||
							
								
								
									
										10
									
								
								session.go
								
								
								
								
							
							
						
						
									
										10
									
								
								session.go
								
								
								
								
							|  | @ -378,14 +378,10 @@ func (session *sessionData) handleResize(data DataObject) { | |||
| 				} | ||||
| 				if viewID, ok := obj.PropertyValue("id"); ok { | ||||
| 					if n := strings.IndexRune(viewID, '-'); n > 0 { | ||||
| 						if index, err := strconv.Atoi(viewID[n+1:]); err == nil { | ||||
| 							if view := session.viewByHTMLID(viewID[:n]); view != nil { | ||||
| 								view.onItemResize(view, index, getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height")) | ||||
| 							} else { | ||||
| 								ErrorLogF(`View with id == %s not found`, viewID[:n]) | ||||
| 							} | ||||
| 						if view := session.viewByHTMLID(viewID[:n]); view != nil { | ||||
| 							view.onItemResize(view, viewID[n+1:], getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height")) | ||||
| 						} else { | ||||
| 							ErrorLogF(`Invalid view id == %s not found`, viewID) | ||||
| 							ErrorLogF(`View with id == %s not found`, viewID[:n]) | ||||
| 						} | ||||
| 					} else if view := session.viewByHTMLID(viewID); view != nil { | ||||
| 						view.onResize(view, getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height")) | ||||
|  |  | |||
							
								
								
									
										344
									
								
								tableView.go
								
								
								
								
							
							
						
						
									
										344
									
								
								tableView.go
								
								
								
								
							|  | @ -214,11 +214,14 @@ type TableView interface { | |||
| 	View | ||||
| 	ParanetView | ||||
| 	ReloadTableData() | ||||
| 	CellFrame(row, column int) Frame | ||||
| 	getCurrent() CellIndex | ||||
| } | ||||
| 
 | ||||
| type tableViewData struct { | ||||
| 	viewData | ||||
| 	cellViews                                 []View | ||||
| 	cellFrame                                 []Frame | ||||
| 	cellSelectedListener, cellClickedListener []func(TableView, int, int) | ||||
| 	rowSelectedListener, rowClickedListener   []func(TableView, int) | ||||
| 	current                                   CellIndex | ||||
|  | @ -245,6 +248,7 @@ func (table *tableViewData) Init(session Session) { | |||
| 	table.viewData.Init(session) | ||||
| 	table.tag = "TableView" | ||||
| 	table.cellViews = []View{} | ||||
| 	table.cellFrame = []Frame{} | ||||
| 	table.cellSelectedListener = []func(TableView, int, int){} | ||||
| 	table.cellClickedListener = []func(TableView, int, int){} | ||||
| 	table.rowSelectedListener = []func(TableView, int){} | ||||
|  | @ -270,6 +274,10 @@ func (table *tableViewData) normalizeTag(tag string) string { | |||
| 	return tag | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) Focusable() bool { | ||||
| 	return GetSelectionMode(table, "") != NoneSelection | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) Get(tag string) interface{} { | ||||
| 	return table.get(table.normalizeTag(tag)) | ||||
| } | ||||
|  | @ -312,6 +320,10 @@ func (table *tableViewData) remove(tag string) { | |||
| 		table.current.Column = -1 | ||||
| 		table.propertyChanged(tag) | ||||
| 
 | ||||
| 	case SelectionMode: | ||||
| 		table.viewData.remove(tag) | ||||
| 		table.propertyChanged(tag) | ||||
| 
 | ||||
| 	default: | ||||
| 		table.viewData.remove(tag) | ||||
| 	} | ||||
|  | @ -486,14 +498,51 @@ func (table *tableViewData) set(tag string, value interface{}) bool { | |||
| 		} | ||||
| 
 | ||||
| 	case Current: | ||||
| 		switch GetSelectionMode(table, "") { | ||||
| 		case NoneSelection: | ||||
| 		switch value := value.(type) { | ||||
| 		case int: | ||||
| 			table.current.Row = value | ||||
| 			table.current.Column = -1 | ||||
| 
 | ||||
| 		case CellSelection: | ||||
| 			// TODO
 | ||||
| 		case CellIndex: | ||||
| 			table.current = value | ||||
| 
 | ||||
| 		case RowSelection: | ||||
| 			// TODO
 | ||||
| 		case DataObject: | ||||
| 			if row, ok := dataIntProperty(value, "row"); ok { | ||||
| 				table.current.Row = row | ||||
| 			} | ||||
| 			if column, ok := dataIntProperty(value, "column"); ok { | ||||
| 				table.current.Column = column | ||||
| 			} | ||||
| 
 | ||||
| 		case string: | ||||
| 			if strings.Contains(value, ",") { | ||||
| 				if values := strings.Split(value, ","); len(values) == 2 { | ||||
| 					var n = []int{0, 0} | ||||
| 					for i := 0; i < 2; i++ { | ||||
| 						var err error | ||||
| 						if n[i], err = strconv.Atoi(values[i]); err != nil { | ||||
| 							ErrorLog(err.Error()) | ||||
| 							return false | ||||
| 						} | ||||
| 					} | ||||
| 					table.current.Row = n[0] | ||||
| 					table.current.Column = n[1] | ||||
| 				} else { | ||||
| 					notCompatibleType(tag, value) | ||||
| 				} | ||||
| 			} else { | ||||
| 				n, err := strconv.Atoi(value) | ||||
| 				if err != nil { | ||||
| 					ErrorLog(err.Error()) | ||||
| 					return false | ||||
| 				} | ||||
| 				table.current.Row = n | ||||
| 				table.current.Column = -1 | ||||
| 			} | ||||
| 
 | ||||
| 		default: | ||||
| 			notCompatibleType(tag, value) | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
|  | @ -524,11 +573,75 @@ func (table *tableViewData) propertyChanged(tag string) { | |||
| 				updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session) | ||||
| 				updateCSSProperty(htmlID, "border-collapse", "separate", session) | ||||
| 			} | ||||
| 
 | ||||
| 		case SelectionMode: | ||||
| 			htmlID := table.htmlID() | ||||
| 			session := table.Session() | ||||
| 
 | ||||
| 			switch GetSelectionMode(table, "") { | ||||
| 			case CellSelection: | ||||
| 				updateProperty(htmlID, "tabindex", "0", session) | ||||
| 				updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session) | ||||
| 				updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)", session) | ||||
| 				updateProperty(htmlID, "data-selection", "cell", session) | ||||
| 				updateProperty(htmlID, "data-focusitemstyle", table.currentStyle(), session) | ||||
| 				updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle(), session) | ||||
| 
 | ||||
| 				if table.current.Row >= 0 && table.current.Column >= 0 { | ||||
| 					updateProperty(htmlID, "data-current", table.cellID(table.current.Row, table.current.Column), session) | ||||
| 				} else { | ||||
| 					removeProperty(htmlID, "data-current", session) | ||||
| 				} | ||||
| 				updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)", session) | ||||
| 
 | ||||
| 			case RowSelection: | ||||
| 				updateProperty(htmlID, "tabindex", "0", session) | ||||
| 				updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session) | ||||
| 				updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)", session) | ||||
| 				updateProperty(htmlID, "data-selection", "cell", session) | ||||
| 				updateProperty(htmlID, "data-focusitemstyle", table.currentStyle(), session) | ||||
| 				updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle(), session) | ||||
| 
 | ||||
| 				if table.current.Row >= 0 { | ||||
| 					updateProperty(htmlID, "data-current", table.rowID(table.current.Row), session) | ||||
| 				} else { | ||||
| 					removeProperty(htmlID, "data-current", session) | ||||
| 				} | ||||
| 				updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)", session) | ||||
| 
 | ||||
| 			default: // NoneSelection
 | ||||
| 				for _, prop := range []string{"tabindex", "data-current", "onfocus", "onblur", "onkeydown", "data-selection"} { | ||||
| 					removeProperty(htmlID, prop, session) | ||||
| 				} | ||||
| 			} | ||||
| 			updateInnerHTML(htmlID, session) | ||||
| 		} | ||||
| 	} | ||||
| 	table.propertyChangedEvent(tag) | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) currentStyle() string { | ||||
| 	if value := table.getRaw(CurrentStyle); value != nil { | ||||
| 		if style, ok := value.(string); ok { | ||||
| 			if style, ok = table.session.resolveConstants(style); ok { | ||||
| 				return style | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "ruiCurrentTableCellFocused" | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) currentInactiveStyle() string { | ||||
| 	if value := table.getRaw(CurrentInactiveStyle); value != nil { | ||||
| 		if style, ok := value.(string); ok { | ||||
| 			if style, ok = table.session.resolveConstants(style); ok { | ||||
| 				return style | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return "ruiCurrentTableCell" | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) valueToCellListeners(value interface{}) []func(TableView, int, int) { | ||||
| 	if value == nil { | ||||
| 		return []func(TableView, int, int){} | ||||
|  | @ -643,16 +756,69 @@ func (table *tableViewData) htmlTag() string { | |||
| 	return "table" | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | ||||
| 	table.cellViews = []View{} | ||||
| func (table *tableViewData) rowID(index int) string { | ||||
| 	return fmt.Sprintf("%s-%d", table.htmlID(), index) | ||||
| } | ||||
| 
 | ||||
| 	content := table.getRaw(Content) | ||||
| 	if content == nil { | ||||
| 		return | ||||
| func (table *tableViewData) cellID(row, column int) string { | ||||
| 	return fmt.Sprintf("%s-%d-%d", table.htmlID(), row, column) | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) { | ||||
| 
 | ||||
| 	if content := table.content(); content != nil { | ||||
| 		buffer.WriteString(` data-rows="`) | ||||
| 		buffer.WriteString(strconv.Itoa(content.RowCount())) | ||||
| 		buffer.WriteString(`" data-columns="`) | ||||
| 		buffer.WriteString(strconv.Itoa(content.ColumnCount())) | ||||
| 		buffer.WriteRune('"') | ||||
| 	} | ||||
| 
 | ||||
| 	adapter, ok := content.(TableAdapter) | ||||
| 	if !ok { | ||||
| 	if selectionMode := GetSelectionMode(table, ""); selectionMode != NoneSelection { | ||||
| 		buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`) | ||||
| 		buffer.WriteString(table.currentStyle()) | ||||
| 		buffer.WriteString(`" data-bluritemstyle="`) | ||||
| 		buffer.WriteString(table.currentInactiveStyle()) | ||||
| 		buffer.WriteRune('"') | ||||
| 
 | ||||
| 		switch selectionMode { | ||||
| 		case RowSelection: | ||||
| 			buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`) | ||||
| 			if table.current.Row >= 0 { | ||||
| 				buffer.WriteString(` data-current="`) | ||||
| 				buffer.WriteString(table.rowID(table.current.Row)) | ||||
| 				buffer.WriteRune('"') | ||||
| 			} | ||||
| 
 | ||||
| 		case CellSelection: | ||||
| 			buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`) | ||||
| 			if table.current.Row >= 0 && table.current.Column >= 0 { | ||||
| 				buffer.WriteString(` data-current="`) | ||||
| 				buffer.WriteString(table.cellID(table.current.Row, table.current.Column)) | ||||
| 				buffer.WriteRune('"') | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	table.viewData.htmlProperties(self, buffer) | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) content() TableAdapter { | ||||
| 	if content := table.getRaw(Content); content != nil { | ||||
| 		if adapter, ok := content.(TableAdapter); ok { | ||||
| 			return adapter | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | ||||
| 	table.cellViews = []View{} | ||||
| 	table.cellFrame = []Frame{} | ||||
| 
 | ||||
| 	adapter := table.content() | ||||
| 	if adapter == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
|  | @ -662,10 +828,12 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	table.cellFrame = make([]Frame, rowCount*columnCount) | ||||
| 
 | ||||
| 	rowStyle := table.getRowStyle() | ||||
| 
 | ||||
| 	var cellStyle1 TableCellStyle = nil | ||||
| 	if style, ok := content.(TableCellStyle); ok { | ||||
| 	if style, ok := adapter.(TableCellStyle); ok { | ||||
| 		cellStyle1 = style | ||||
| 	} | ||||
| 
 | ||||
|  | @ -691,6 +859,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| 	view.Init(session) | ||||
| 
 | ||||
| 	ignorCells := []struct{ row, column int }{} | ||||
| 	selectionMode := GetSelectionMode(table, "") | ||||
| 
 | ||||
| 	tableCSS := func(startRow, endRow int, cellTag string, cellBorder BorderProperty, cellPadding BoundsProperty) { | ||||
| 		for row := startRow; row < endRow; row++ { | ||||
|  | @ -706,14 +875,31 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if cssBuilder.buffer.Len() > 0 { | ||||
| 				buffer.WriteString(`<tr style="`) | ||||
| 				buffer.WriteString(cssBuilder.buffer.String()) | ||||
| 				buffer.WriteString(`">`) | ||||
| 			} else { | ||||
| 				buffer.WriteString("<tr>") | ||||
| 			buffer.WriteString(`<tr id="`) | ||||
| 			buffer.WriteString(table.rowID(row)) | ||||
| 			buffer.WriteRune('"') | ||||
| 
 | ||||
| 			if selectionMode == RowSelection { | ||||
| 				if row == table.current.Row { | ||||
| 					buffer.WriteString(` class="`) | ||||
| 					if table.HasFocus() { | ||||
| 						buffer.WriteString(table.currentStyle()) | ||||
| 					} else { | ||||
| 						buffer.WriteString(table.currentInactiveStyle()) | ||||
| 					} | ||||
| 					buffer.WriteRune('"') | ||||
| 				} | ||||
| 
 | ||||
| 				buffer.WriteString(` onclick="tableRowClickEvent(this, event)"`) | ||||
| 			} | ||||
| 
 | ||||
| 			if cssBuilder.buffer.Len() > 0 { | ||||
| 				buffer.WriteString(` style="`) | ||||
| 				buffer.WriteString(cssBuilder.buffer.String()) | ||||
| 				buffer.WriteString(`"`) | ||||
| 			} | ||||
| 			buffer.WriteString(">") | ||||
| 
 | ||||
| 			for column := 0; column < columnCount; column++ { | ||||
| 				ignore := false | ||||
| 				for _, cell := range ignorCells { | ||||
|  | @ -748,7 +934,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| 											return value | ||||
| 
 | ||||
| 										case string: | ||||
| 											if value, ok = session.resolveConstants(value); ok { | ||||
| 											if value, ok := session.resolveConstants(value); ok { | ||||
| 												if n, err := strconv.Atoi(value); err == nil { | ||||
| 													return n | ||||
| 												} | ||||
|  | @ -780,6 +966,23 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) { | |||
| 
 | ||||
| 					buffer.WriteRune('<') | ||||
| 					buffer.WriteString(cellTag) | ||||
| 					buffer.WriteString(` id="`) | ||||
| 					buffer.WriteString(table.cellID(row, column)) | ||||
| 					buffer.WriteString(`" class="ruiView`) | ||||
| 
 | ||||
| 					if selectionMode == CellSelection && row == table.current.Row && column == table.current.Column { | ||||
| 						buffer.WriteRune(' ') | ||||
| 						if table.HasFocus() { | ||||
| 							buffer.WriteString(table.currentStyle()) | ||||
| 						} else { | ||||
| 							buffer.WriteString(table.currentInactiveStyle()) | ||||
| 						} | ||||
| 					} | ||||
| 					buffer.WriteRune('"') | ||||
| 
 | ||||
| 					if selectionMode == CellSelection { | ||||
| 						buffer.WriteString(` onclick="tableCellClickEvent(this, event)"`) | ||||
| 					} | ||||
| 
 | ||||
| 					if columnSpan > 1 { | ||||
| 						buffer.WriteString(` colspan="`) | ||||
|  | @ -1087,6 +1290,10 @@ func (table *tableViewData) getCellBorder() BorderProperty { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) getCurrent() CellIndex { | ||||
| 	return table.current | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) cssStyle(self View, builder cssBuilder) { | ||||
| 	table.viewData.cssViewStyle(builder, table.Session()) | ||||
| 
 | ||||
|  | @ -1101,30 +1308,93 @@ func (table *tableViewData) cssStyle(self View, builder cssBuilder) { | |||
| } | ||||
| 
 | ||||
| func (table *tableViewData) ReloadTableData() { | ||||
| 	if content := table.content(); content != nil { | ||||
| 		updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session()) | ||||
| 		updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.Session()) | ||||
| 	} | ||||
| 	updateInnerHTML(table.htmlID(), table.Session()) | ||||
| } | ||||
| 
 | ||||
| func (cell *tableCellView) Set(tag string, value interface{}) bool { | ||||
| 	return cell.set(strings.ToLower(tag), value) | ||||
| func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) { | ||||
| 	if n := strings.IndexRune(index, '-'); n > 0 { | ||||
| 		if row, err := strconv.Atoi(index[:n]); err == nil { | ||||
| 			if column, err := strconv.Atoi(index[n+1:]); err == nil { | ||||
| 				if content := table.content(); content != nil { | ||||
| 					i := row*content.ColumnCount() + column | ||||
| 					if i < len(table.cellFrame) { | ||||
| 						table.cellFrame[i].Left = x | ||||
| 						table.cellFrame[i].Top = y | ||||
| 						table.cellFrame[i].Width = width | ||||
| 						table.cellFrame[i].Height = height | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				ErrorLog(err.Error()) | ||||
| 			} | ||||
| 		} else { | ||||
| 			ErrorLog(err.Error()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		ErrorLogF(`Invalid cell index: %s`, index) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (cell *tableCellView) set(tag string, value interface{}) bool { | ||||
| 	switch tag { | ||||
| 	case VerticalAlign: | ||||
| 		tag = TableVerticalAlign | ||||
| 	} | ||||
| 	return cell.viewData.set(tag, value) | ||||
| } | ||||
| 
 | ||||
| func (cell *tableCellView) cssStyle(self View, builder cssBuilder) { | ||||
| 	session := cell.Session() | ||||
| 	cell.viewData.cssViewStyle(builder, session) | ||||
| 
 | ||||
| 	if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok { | ||||
| 		builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value]) | ||||
| func (table *tableViewData) CellFrame(row, column int) Frame { | ||||
| 	if content := table.content(); content != nil { | ||||
| 		i := row*content.ColumnCount() + column | ||||
| 		if i < len(table.cellFrame) { | ||||
| 			return table.cellFrame[i] | ||||
| 		} | ||||
| 	} | ||||
| 	return Frame{} | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) Views() []View { | ||||
| 	return table.cellViews | ||||
| } | ||||
| 
 | ||||
| func (table *tableViewData) handleCommand(self View, command string, data DataObject) bool { | ||||
| 	switch command { | ||||
| 	case "currentRow": | ||||
| 		if row, ok := dataIntProperty(data, "row"); ok && row != table.current.Row { | ||||
| 			table.current.Row = row | ||||
| 			for _, listener := range table.rowSelectedListener { | ||||
| 				listener(table, row) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	case "currentCell": | ||||
| 		if row, ok := dataIntProperty(data, "row"); ok { | ||||
| 			if column, ok := dataIntProperty(data, "column"); ok { | ||||
| 				if row != table.current.Row || column != table.current.Column { | ||||
| 					table.current.Row = row | ||||
| 					table.current.Column = column | ||||
| 					for _, listener := range table.cellSelectedListener { | ||||
| 						listener(table, row, column) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	case "rowClick": | ||||
| 		if row, ok := dataIntProperty(data, "row"); ok { | ||||
| 			for _, listener := range table.rowClickedListener { | ||||
| 				listener(table, row) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	case "cellClick": | ||||
| 		if row, ok := dataIntProperty(data, "row"); ok { | ||||
| 			if column, ok := dataIntProperty(data, "column"); ok { | ||||
| 				for _, listener := range table.cellClickedListener { | ||||
| 					listener(table, row, column) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		return table.viewData.handleCommand(self, command, data) | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,28 @@ | |||
| package rui | ||||
| 
 | ||||
| import "strings" | ||||
| 
 | ||||
| func (cell *tableCellView) Set(tag string, value interface{}) bool { | ||||
| 	return cell.set(strings.ToLower(tag), value) | ||||
| } | ||||
| 
 | ||||
| func (cell *tableCellView) set(tag string, value interface{}) bool { | ||||
| 	switch tag { | ||||
| 	case VerticalAlign: | ||||
| 		tag = TableVerticalAlign | ||||
| 	} | ||||
| 	return cell.viewData.set(tag, value) | ||||
| } | ||||
| 
 | ||||
| func (cell *tableCellView) cssStyle(self View, builder cssBuilder) { | ||||
| 	session := cell.Session() | ||||
| 	cell.viewData.cssViewStyle(builder, session) | ||||
| 
 | ||||
| 	if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok { | ||||
| 		builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value]) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetSelectionMode returns the mode of the TableView elements selection.
 | ||||
| // Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
 | ||||
| // If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | ||||
|  | @ -15,6 +38,42 @@ func GetSelectionMode(view View, subviewID string) int { | |||
| 	return NoneSelection | ||||
| } | ||||
| 
 | ||||
| // GetSelectionMode returns the index of the TableView selected row.
 | ||||
| // If there is no selected row, then a value less than 0 are returned.
 | ||||
| // If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | ||||
| func GetCurrentTableRow(view View, subviewID string) int { | ||||
| 	if subviewID != "" { | ||||
| 		view = ViewByID(view, subviewID) | ||||
| 	} | ||||
| 
 | ||||
| 	if view != nil { | ||||
| 		if selectionMode := GetSelectionMode(view, ""); selectionMode != NoneSelection { | ||||
| 			if tableView, ok := view.(TableView); ok { | ||||
| 				return tableView.getCurrent().Row | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
| 
 | ||||
| // GetCurrentTableCell returns the row and column index of the TableView selected cell.
 | ||||
| // If there is no selected cell, then a value of the row and column index less than 0 is returned.
 | ||||
| // If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | ||||
| func GetCurrentTableCell(view View, subviewID string) CellIndex { | ||||
| 	if subviewID != "" { | ||||
| 		view = ViewByID(view, subviewID) | ||||
| 	} | ||||
| 
 | ||||
| 	if view != nil { | ||||
| 		if selectionMode := GetSelectionMode(view, ""); selectionMode != NoneSelection { | ||||
| 			if tableView, ok := view.(TableView); ok { | ||||
| 				return tableView.getCurrent() | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return CellIndex{Row: -1, Column: -1} | ||||
| } | ||||
| 
 | ||||
| // GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
 | ||||
| // If there are no listeners then the empty list is returned.
 | ||||
| // If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
 | ||||
|  |  | |||
|  | @ -44,6 +44,10 @@ func (picker *timePickerData) Init(session Session) { | |||
| 	picker.timeChangedListeners = []func(TimePicker, time.Time){} | ||||
| } | ||||
| 
 | ||||
| func (picker *timePickerData) Focusable() bool { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (picker *timePickerData) normalizeTag(tag string) string { | ||||
| 	tag = strings.ToLower(tag) | ||||
| 	switch tag { | ||||
|  |  | |||
							
								
								
									
										18
									
								
								view.go
								
								
								
								
							
							
						
						
									
										18
									
								
								view.go
								
								
								
								
							|  | @ -58,6 +58,8 @@ type View interface { | |||
| 	SetAnimated(tag string, value interface{}, animation Animation) bool | ||||
| 	// SetChangeListener set the function to track the change of the View property
 | ||||
| 	SetChangeListener(tag string, listener func(View, string)) | ||||
| 	// HasFocus returns 'true' if the view has focus
 | ||||
| 	HasFocus() bool | ||||
| 
 | ||||
| 	handleCommand(self View, command string, data DataObject) bool | ||||
| 	htmlClass(disabled bool) string | ||||
|  | @ -73,7 +75,7 @@ type View interface { | |||
| 	getTransitions() Params | ||||
| 
 | ||||
| 	onResize(self View, x, y, width, height float64) | ||||
| 	onItemResize(self View, index int, x, y, width, height float64) | ||||
| 	onItemResize(self View, index string, x, y, width, height float64) | ||||
| 	setNoResizeEvent() | ||||
| 	isNoResizeEvent() bool | ||||
| 	setScroll(x, y, width, height float64) | ||||
|  | @ -95,6 +97,7 @@ type viewData struct { | |||
| 	scroll           Frame | ||||
| 	noResizeEvent    bool | ||||
| 	created          bool | ||||
| 	hasFocus         bool | ||||
| 	//animation map[string]AnimationEndListener
 | ||||
| } | ||||
| 
 | ||||
|  | @ -737,7 +740,14 @@ func (view *viewData) handleCommand(self View, command string, data DataObject) | |||
| 	case TouchStart, TouchEnd, TouchMove, TouchCancel: | ||||
| 		handleTouchEvents(self, command, data) | ||||
| 
 | ||||
| 	case FocusEvent, LostFocusEvent: | ||||
| 	case FocusEvent: | ||||
| 		view.hasFocus = true | ||||
| 		for _, listener := range getFocusListeners(view, "", command) { | ||||
| 			listener(self) | ||||
| 		} | ||||
| 
 | ||||
| 	case LostFocusEvent: | ||||
| 		view.hasFocus = false | ||||
| 		for _, listener := range getFocusListeners(view, "", command) { | ||||
| 			listener(self) | ||||
| 		} | ||||
|  | @ -838,3 +848,7 @@ func (view *viewData) SetChangeListener(tag string, listener func(View, string)) | |||
| 		view.changeListener[tag] = listener | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (view *viewData) HasFocus() bool { | ||||
| 	return view.hasFocus | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue