diff --git a/interface/resources/fonts/FiraSans-Regular.ttf b/interface/resources/fonts/FiraSans-Regular.ttf new file mode 100644 index 0000000000..d9fdc0e922 Binary files /dev/null and b/interface/resources/fonts/FiraSans-Regular.ttf differ diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 1b0d4f3fe6..89d4767012 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controls-uit/AttachmentsTable.qml index 6022de7f93..ce93b8f4df 100644 --- a/interface/resources/qml/controls-uit/AttachmentsTable.qml +++ b/interface/resources/qml/controls-uit/AttachmentsTable.qml @@ -21,7 +21,6 @@ import "../hifi/models" TableView { id: tableView - // property var tableModel: ListModel { } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light @@ -46,7 +45,7 @@ TableView { RalewayRegular { id: textHeader - size: hifi.fontSizes.tableText + size: hifi.fontSizes.tableHeading color: hifi.colors.lightGrayText text: styleData.value anchors { @@ -87,7 +86,7 @@ TableView { bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: hifi.colors.tableScrollHandleDark } } @@ -107,7 +106,7 @@ TableView { margins: 1 // Shrink } radius: 4 - color: hifi.colors.tableScrollBackground + color: hifi.colors.tableScrollBackgroundDark } } diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index f99e18b12b..cd6dc8ede0 100755 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -91,7 +91,7 @@ FocusScope { HiFiGlyphs { anchors { top: parent.top - topMargin: -8 + topMargin: -11 horizontalCenter: parent.horizontalCenter } size: hifi.dimensions.spinnerSize diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index c4ee53c04f..ac353b5a52 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -17,8 +17,9 @@ import "../styles-uit" Original.Button { property int color: 0 - property int colorScheme: hifi.colorShemes.light + property int colorScheme: hifi.colorSchemes.light property string glyph: "" + property int size: 32 width: 120 height: 28 @@ -65,7 +66,13 @@ Original.Button { : hifi.buttons.disabledTextColor[control.colorScheme] verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + anchors { + // Tweak horizontal alignment so that it looks right. + left: parent.left + leftMargin: -0.5 + } text: control.glyph + size: control.size } } } diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 599d94c28d..5d78cfc5a8 100755 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -56,7 +56,7 @@ SpinBox { incrementControl: HiFiGlyphs { id: incrementButton text: hifi.glyphs.caratUp - x: 6 + x: 10 y: 1 size: hifi.dimensions.spinnerSize color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray @@ -64,8 +64,8 @@ SpinBox { decrementControl: HiFiGlyphs { text: hifi.glyphs.caratDn - x: 6 - y: -3 + x: 10 + y: -1 size: hifi.dimensions.spinnerSize color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray } diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index de6950c07e..35029ad8bf 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -17,20 +17,68 @@ import "../styles-uit" TableView { id: tableView - property var tableModel: ListModel { } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property bool expandSelectedRow: false - model: tableModel - - TableViewColumn { - role: "name" - } - - anchors { left: parent.left; right: parent.right } + model: ListModel { } headerVisible: false - headerDelegate: Item { } // Fix OSX QML bug that displays scrollbar starting too low. + headerDelegate: Rectangle { + height: hifi.dimensions.tableHeaderHeight + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + + RalewayRegular { + id: titleText + text: styleData.value + size: hifi.fontSizes.tableHeading + font.capitalization: Font.AllUppercase + color: hifi.colors.baseGrayHighlight + anchors { + left: parent.left + leftMargin: hifi.dimensions.tablePadding + verticalCenter: parent.verticalCenter + } + } + + HiFiGlyphs { + id: titleSort + text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn + color: hifi.colors.baseGrayHighlight + size: hifi.fontSizes.tableHeadingIcon + anchors { + left: titleText.right + leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 + right: parent.right + rightMargin: hifi.dimensions.tablePadding + verticalCenter: titleText.verticalCenter + } + visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column + } + + Rectangle { + width: 1 + anchors { + left: parent.left + top: parent.top + topMargin: 1 + bottom: parent.bottom + bottomMargin: 2 + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: styleData.column > 0 + } + + Rectangle { + height: 1 + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + } + } // Use rectangle to draw border with rounded corners. frameVisible: false @@ -50,8 +98,10 @@ TableView { style: TableViewStyle { // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven - alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 handle: Item { id: scrollbarHandle @@ -59,33 +109,38 @@ TableView { Rectangle { anchors { fill: parent + topMargin: 3 + bottomMargin: 3 // "" leftMargin: 2 // Move it right rightMargin: -2 // "" - topMargin: 3 // Shrink vertically - bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 10 + implicitWidth: 9 Rectangle { anchors { fill: parent margins: -1 // Expand + topMargin: headerVisible ? -hifi.dimensions.tableHeaderHeight : -1 } - color: hifi.colors.baseGrayHighlight - } + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - Rectangle { - anchors { - fill: parent - margins: 1 // Shrink + Rectangle { + // Extend header bottom border + anchors { + top: parent.top + topMargin: hifi.dimensions.tableHeaderHeight - 1 + left: parent.left + right: parent.right + } + height: 1 + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: headerVisible } - radius: 4 - color: hifi.colors.tableScrollBackground } } @@ -99,85 +154,11 @@ TableView { } rowDelegate: Rectangle { - height: (styleData.selected ? 1.8 : 1) * hifi.dimensions.tableRowHeight + height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight color: styleData.selected ? hifi.colors.primaryHighlight : tableView.isLightColorScheme ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) } - - itemDelegate: Item { - anchors { - left: parent ? parent.left : undefined - leftMargin: hifi.dimensions.tablePadding - right: parent ? parent.right : undefined - rightMargin: hifi.dimensions.tablePadding - } - - FiraSansSemiBold { - id: textItem - text: styleData.value - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - anchors { - left: parent.left - right: parent.right - top: parent.top - topMargin: 3 - } - - // FIXME: Put reload item in tableModel passed in from RunningScripts. - HiFiGlyphs { - id: reloadButton - text: hifi.glyphs.reloadSmall - color: reloadButtonArea.pressed ? hifi.colors.white : parent.color - anchors { - top: parent.top - right: stopButton.left - verticalCenter: parent.verticalCenter - } - MouseArea { - id: reloadButtonArea - anchors { fill: parent; margins: -2 } - onClicked: reloadScript(model.url) - } - } - - // FIXME: Put stop item in tableModel passed in from RunningScripts. - HiFiGlyphs { - id: stopButton - text: hifi.glyphs.closeSmall - color: stopButtonArea.pressed ? hifi.colors.white : parent.color - anchors { - top: parent.top - right: parent.right - verticalCenter: parent.verticalCenter - } - MouseArea { - id: stopButtonArea - anchors { fill: parent; margins: -2 } - onClicked: stopScript(model.url) - } - } - } - - // FIXME: Automatically use aux. information from tableModel - FiraSansSemiBold { - text: tableModel.get(styleData.row) ? tableModel.get(styleData.row).url : "" - elide: Text.ElideMiddle - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - anchors { - top: textItem.bottom - left: parent.left - right: parent.right - } - visible: styleData.selected - } - } } diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 6d4d202e2c..aa1d10f030 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -1,5 +1,5 @@ // -// Table.qml +// Tree.qml // // Created by David Rowe on 17 Feb 2016 // Copyright 2016 High Fidelity, Inc. @@ -85,27 +85,18 @@ TreeView { bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 10 + implicitWidth: 9 Rectangle { anchors { fill: parent margins: -1 // Expand } - color: hifi.colors.baseGrayHighlight - } - - Rectangle { - anchors { - fill: parent - margins: 1 // Shrink - } - radius: 4 - color: hifi.colors.tableScrollBackground + color: hifi.colors.tableBackgroundDark } } diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 916ef434b6..5cd972a38f 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -1,4 +1,14 @@ -import QtQuick 2.0 +// +// FileDialog.qml +// +// Created by Bradley Austin Davis on 14 Jan 2016 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 import QtQuick.Controls 1.4 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 @@ -6,17 +16,20 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import ".." -import "../windows" -import "../styles" -import "../controls" as VrControls +import "../controls-uit" +import "../styles-uit" +import "../windows-uit" + import "fileDialog" //FIXME implement shortcuts for favorite location ModalWindow { id: root - resizable: true - width: 640 - height: 480 + //resizable: true + implicitWidth: 640 + implicitHeight: 480 + + HifiConstants { id: hifi } Settings { category: "FileDialog" @@ -30,12 +43,14 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property alias caption: root.title; // Set from OffscreenUi::getOpenFile() - property alias dir: model.folder; + property alias dir: fileTableModel.folder; // Set from OffscreenUi::getOpenFile() property alias filter: selectionType.filtersString; // Set from OffscreenUi::getOpenFile() property int options; // <-- FIXME unused + property string iconText: text !== "" ? hifi.glyphs.scriptUpload : "" + property int iconSize: 40 property bool selectDirectory: false; property bool showHidden: false; @@ -46,77 +61,120 @@ ModalWindow { property alias model: fileTableView.model property var drives: helper.drives() + property int titleWidth: 0 + signal selectedFile(var file); signal canceled(); Component.onCompleted: { console.log("Helper " + helper + " drives " + drives) - drivesSelector.onCurrentTextChanged.connect(function(){ - root.dir = helper.pathToUrl(drivesSelector.currentText); - }) + + // HACK: The following lines force the model to initialize properly such that the go-up button + // works properly from the initial screen. + var initialFolder = folderListModel.folder; + fileTableModel.folder = helper.pathToUrl(drives[0]); + fileTableModel.folder = initialFolder; + + iconText = root.title !== "" ? hifi.glyphs.scriptUpload : ""; } - Rectangle { - anchors.fill: parent - color: "white" + Item { + clip: true + width: pane.width + height: pane.height + anchors.margins: 0 Row { id: navControls - anchors { left: parent.left; top: parent.top; margins: 8 } - spacing: 8 - // FIXME implement back button - //VrControls.ButtonAwesome { - // id: backButton - // text: "\uf0a8" - // size: currentDirectory.height - // enabled: d.backStack.length != 0 - // MouseArea { anchors.fill: parent; onClicked: d.navigateBack() } - //} - VrControls.ButtonAwesome { + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: parent.left + } + spacing: hifi.dimensions.contentSpacing.x + + GlyphButton { id: upButton - enabled: model.parentFolder && model.parentFolder !== "" - text: "\uf0aa" - size: 32 + glyph: hifi.glyphs.levelUp + width: height + size: 30 + enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); } - VrControls.ButtonAwesome { + + GlyphButton { id: homeButton property var destination: helper.home(); + glyph: hifi.glyphs.home + size: 28 + width: height enabled: d.homeDestination ? true : false - text: "\uf015" - size: 32 onClicked: d.navigateHome(); } - - VrControls.ComboBox { - id: drivesSelector - width: 48 - height: homeButton.height - model: drives - visible: drives.length > 1 - currentIndex: 0 - - } } - TextField { - id: currentDirectory - height: homeButton.height - style: TextFieldStyle { renderType: Text.QtRendering } - anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 } - property var lastValidFolder: helper.urlToPath(model.folder) - onLastValidFolderChanged: text = lastValidFolder; - verticalAlignment: Text.AlignVCenter - font.pointSize: 14 - font.bold: true + ComboBox { + id: pathSelector + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: navControls.right + leftMargin: hifi.dimensions.contentSpacing.x + right: parent.right + } - // FIXME add support auto-completion - onAccepted: { - if (!helper.validFolder(text)) { - text = lastValidFolder; - return + property var lastValidFolder: helper.urlToPath(fileTableModel.folder) + + function calculatePathChoices(folder) { + var folders = folder.split("/"), + choices = [], + i, length; + + if (folders[folders.length - 1] === "") { + folders.pop(); + } + + choices.push(folders[0]); + + for (i = 1, length = folders.length; i < length; i++) { + choices.push(choices[i - 1] + "/" + folders[i]); + } + + if (folders[0] === "") { + // Special handling for OSX root dir. + choices[0] = "/"; + } + + choices.reverse(); + + if (drives && drives.length > 1) { + choices.push("This PC"); + } + + if (choices.length > 0) { + pathSelector.model = choices; + } + } + + onLastValidFolderChanged: { + var folder = d.capitalizeDrive(lastValidFolder); + calculatePathChoices(folder); + } + + onCurrentTextChanged: { + var folder = currentText; + + if (/^[a-zA-z]:$/.test(folder)) { + folder = "file:///" + folder + "/"; + } else if (folder === "This PC") { + folder = "file:///"; + } else { + folder = helper.pathToUrl(folder); + } + + if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) { + fileTableModel.folder = folder; } - model.folder = helper.pathToUrl(text); } } @@ -127,67 +185,323 @@ ModalWindow { property bool currentSelectionIsFolder; property var backStack: [] property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } - property var modelConnection: Connections { target: model; onFolderChanged: d.update(); } + property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } property var homeDestination: helper.home(); - Component.onCompleted: update(); + + function capitalizeDrive(path) { + // Consistently capitalize drive letter for Windows. + if (/[a-zA-Z]:/.test(path)) { + return path.charAt(0).toUpperCase() + path.slice(1); + } + return path; + } function update() { var row = fileTableView.currentRow; - if (row === -1 && root.selectDirectory) { - currentSelectionUrl = fileTableView.model.folder; - currentSelectionIsFolder = true; + + if (row === -1) { return; } - currentSelectionUrl = fileTableView.model.get(row, "fileURL"); + currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); currentSelectionIsFolder = fileTableView.model.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { - currentSelection.text = helper.urlToPath(currentSelectionUrl); + currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); } else { - currentSelection.text = "" + currentSelection.text = ""; } } function navigateUp() { - if (model.parentFolder && model.parentFolder !== "") { - model.folder = model.parentFolder + if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { + fileTableModel.folder = fileTableModel.parentFolder; return true; } } function navigateHome() { - model.folder = homeDestination; + fileTableModel.folder = homeDestination; return true; } } - FileTableView { + FolderListModel { + id: folderListModel + nameFilters: selectionType.currentFilter + showDirsFirst: true + showDotAndDotDot: false + showFiles: !root.selectDirectory + Component.onCompleted: { + showFiles = !root.selectDirectory + } + + onFolderChanged: { + fileTableModel.update(); // Update once the data from the folder change is available. + } + + function getItem(index, field) { + return get(index, field); + } + } + + ListModel { + // Emulates FolderListModel but contains drive data. + id: driveListModel + + property int count: 1 + + Component.onCompleted: initialize(); + + function initialize() { + var drive, + i; + + count = drives.length; + + for (i = 0; i < count; i++) { + drive = drives[i].slice(0, -1); // Remove trailing "/". + append({ + fileName: drive, + fileModified: new Date(0), + fileSize: 0, + filePath: drive + "/", + fileIsDir: true, + fileNameSort: drive.toLowerCase() + }); + } + } + + function getItem(index, field) { + return get(index)[field]; + } + } + + ListModel { + id: fileTableModel + + // FolderListModel has a couple of problems: + // 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757 + // 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901 + // + // To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with + // drive information when viewing at the computer level. + + property var folder + property int sortOrder: Qt.AscendingOrder + property int sortColumn: 0 + property var model: folderListModel + property string parentFolder: calculateParentFolder(); + + readonly property string rootFolder: "file:///" + + function calculateParentFolder() { + if (model === folderListModel) { + if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) { + return rootFolder; + } else { + return folderListModel.parentFolder; + } + } else { + return ""; + } + } + + onFolderChanged: { + if (folder === rootFolder) { + model = driveListModel; + update(); + } else { + var needsUpdate = model === driveListModel && folder === folderListModel.folder; + + model = folderListModel; + folderListModel.folder = folder; + + if (needsUpdate) { + update(); + } + } + } + + function isFolder(row) { + if (row === -1) { + return false; + } + return get(row).fileIsDir; + } + + function update() { + var dataFields = ["fileName", "fileModified", "fileSize"], + sortFields = ["fileNameSort", "fileModified", "fileSize"], + dataField = dataFields[sortColumn], + sortField = sortFields[sortColumn], + sortValue, + fileName, + fileIsDir, + comparisonFunction, + lower, + middle, + upper, + rows = 0, + i; + + clear(); + + comparisonFunction = sortOrder === Qt.AscendingOrder + ? function(a, b) { return a < b; } + : function(a, b) { return a > b; } + + for (i = 0; i < model.count; i++) { + fileName = model.getItem(i, "fileName"); + fileIsDir = model.getItem(i, "fileIsDir"); + + sortValue = model.getItem(i, dataField); + if (dataField === "fileName") { + // Directories first by prefixing a "*". + // Case-insensitive. + sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase(); + } + + lower = 0; + upper = rows; + while (lower < upper) { + middle = Math.floor((lower + upper) / 2); + var lessThan; + if (comparisonFunction(sortValue, get(middle)[sortField])) { + lessThan = true; + upper = middle; + } else { + lessThan = false; + lower = middle + 1; + } + } + + insert(lower, { + fileName: fileName, + fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), + fileSize: model.getItem(i, "fileSize"), + filePath: model.getItem(i, "filePath"), + fileIsDir: fileIsDir, + fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase() + }); + + rows++; + } + } + } + + Table { id: fileTableView - anchors { left: parent.left; right: parent.right; top: currentDirectory.bottom; bottom: currentSelection.top; margins: 8 } + colorScheme: hifi.colorSchemes.light + anchors { + top: navControls.bottom + topMargin: hifi.dimensions.contentSpacing.y + left: parent.left + right: parent.right + bottom: currentSelection.top + bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height + } + headerVisible: !selectDirectory onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); - model: FolderListModel { - id: model - nameFilters: selectionType.currentFilter - showDirsFirst: true - showDotAndDotDot: false - showFiles: !root.selectDirectory - // For some reason, declaring these bindings directly in the targets doesn't - // work for setting the initial state - Component.onCompleted: { - currentDirectory.lastValidFolder = Qt.binding(function() { return helper.urlToPath(model.folder); }); - upButton.enabled = Qt.binding(function() { return (model.parentFolder && model.parentFolder != "") ? true : false; }); - showFiles = !root.selectDirectory + + sortIndicatorColumn: 0 + sortIndicatorOrder: Qt.AscendingOrder + sortIndicatorVisible: true + + model: fileTableModel + + function updateSort() { + model.sortOrder = sortIndicatorOrder; + model.sortColumn = sortIndicatorColumn; + model.update(); + } + + onSortIndicatorColumnChanged: { updateSort(); } + + onSortIndicatorOrderChanged: { updateSort(); } + + onActiveFocusChanged: { + if (activeFocus && currentRow == -1) { + fileTableView.selection.select(0) } - onFolderChanged: { - fileTableView.selection.clear(); - fileTableView.selection.select(0); - fileTableView.currentRow = 0; + } + + itemDelegate: Item { + clip: true + + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; } + + FiraSansSemiBold { + text: getText(); + elide: styleData.elideMode + anchors { + left: parent.left + leftMargin: hifi.dimensions.tablePadding + right: parent.right + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent.verticalCenter + } + size: hifi.fontSizes.tableText + color: hifi.colors.baseGrayHighlight + font.family: (styleData.row !== -1 && fileTableView.model.get(styleData.row).fileIsDir) + ? firaSansSemiBold.name : firaSansRegular.name + + function getText() { + if (styleData.row === -1) { + return styleData.value; + } + + switch (styleData.column) { + case 1: return fileTableView.model.get(styleData.row).fileIsDir ? "" : styleData.value; + case 2: return fileTableView.model.get(styleData.row).fileIsDir ? "" : formatSize(styleData.value); + default: return styleData.value; + } + } + function formatSize(size) { + var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]; + var suffixIndex = 0 + while ((size / 1024.0) > 1.1) { + size /= 1024.0; + ++suffixIndex; + } + + size = Math.round(size*1000)/1000; + size = size.toLocaleString() + + return size + " " + suffixes[suffixIndex]; + } } } + TableViewColumn { + id: fileNameColumn + role: "fileName" + title: "Name" + width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width + movable: false + resizable: true + } + TableViewColumn { + id: fileMofifiedColumn + role: "fileModified" + title: "Date" + width: 0.3 * fileTableView.width + movable: false + resizable: true + visible: !selectDirectory + } + TableViewColumn { + role: "fileSize" + title: "Size" + width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width + movable: false + resizable: true + visible: !selectDirectory + } + function navigateToRow(row) { currentRow = row; navigateToCurrentRow(); @@ -196,9 +510,9 @@ ModalWindow { function navigateToCurrentRow() { var row = fileTableView.currentRow var isFolder = model.isFolder(row); - var file = model.get(row, "fileURL"); + var file = model.get(row).filePath; if (isFolder) { - fileTableView.model.folder = file + fileTableView.model.folder = helper.pathToUrl(file); } else { okAction.trigger(); } @@ -213,7 +527,7 @@ ModalWindow { var newPrefix = prefix + event.text.toLowerCase(); var matchedIndex = -1; for (var i = 0; i < model.count; ++i) { - var name = model.get(i, "fileName").toLowerCase(); + var name = model.get(i).fileName.toLowerCase(); if (0 === name.indexOf(newPrefix)) { matchedIndex = i; break; @@ -254,14 +568,19 @@ ModalWindow { } break; } - } } TextField { id: currentSelection - style: TextFieldStyle { renderType: Text.QtRendering } - anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top } + label: "Path:" + anchors { + left: parent.left + right: selectionType.visible ? selectionType.left: parent.right + rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 + bottom: buttonRow.top + bottomMargin: hifi.dimensions.contentSpacing.y + } readOnly: !root.saveDialog activeFocusOnTab: !readOnly onActiveFocusChanged: if (activeFocus) { selectAll(); } @@ -270,27 +589,34 @@ ModalWindow { FileTypeSelection { id: selectionType - anchors { bottom: buttonRow.top; bottomMargin: 8; right: parent.right; rightMargin: 8; left: buttonRow.left } - visible: !selectDirectory + anchors { + top: currentSelection.top + left: buttonRow.left + right: parent.right + } + visible: !selectDirectory && filtersCount > 1 KeyNavigation.left: fileTableView KeyNavigation.right: openButton } Row { id: buttonRow - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.bottom: parent.bottom - anchors.bottomMargin: 8 - spacing: 8 + anchors { + right: parent.right + bottom: parent.bottom + } + spacing: hifi.dimensions.contentSpacing.y + Button { id: openButton + color: hifi.buttons.blue action: okAction Keys.onReturnPressed: okAction.trigger() KeyNavigation.up: selectionType KeyNavigation.left: selectionType KeyNavigation.right: cancelButton } + Button { id: cancelButton action: cancelAction @@ -385,5 +711,3 @@ ModalWindow { } } } - - diff --git a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml b/interface/resources/qml/dialogs/fileDialog/FileTableView.qml deleted file mode 100644 index 93cdeebab0..0000000000 --- a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick 2.0 -import QtQuick.Controls 1.4 - -TableView { - id: root - onActiveFocusChanged: { - if (activeFocus && currentRow == -1) { - root.selection.select(0) - } - } - - itemDelegate: Component { - Item { - clip: true - Text { - x: 3 - anchors.verticalCenter: parent.verticalCenter - color: styleData.textColor - elide: styleData.elideMode - text: getText(); - font.italic: root.model.get(styleData.row, "fileIsDir") ? true : false - - function getText() { - switch (styleData.column) { - //case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss"); - case 2: return root.model.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value); - default: return styleData.value; - } - } - function formatSize(size) { - var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ]; - var suffixIndex = 0 - while ((size / 1024.0) > 1.1) { - size /= 1024.0; - ++suffixIndex; - } - - size = Math.round(size*1000)/1000; - size = size.toLocaleString() - - return size + " " + suffixes[suffixIndex]; - } - } - } - } - - TableViewColumn { - role: "fileName" - title: "Name" - width: 400 - } - TableViewColumn { - role: "fileModified" - title: "Date Modified" - width: 200 - } - TableViewColumn { - role: "fileSize" - title: "Size" - width: 200 - } -} - - diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index 57ad2028ad..50a10974b5 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -1,11 +1,22 @@ +// +// FileTypeSelection.qml +// +// Created by Bradley Austin Davis on 29 Jan 2016 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import QtQuick 2.5 -import "../../controls" as VrControls +import "../../controls-uit" -VrControls.ComboBox { +ComboBox { id: root property string filtersString: "All Files (*.*)"; property var currentFilter: [ "*.*" ]; + property int filtersCount: filtersString.split(';;').length // Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain // multiple filters separated by semicolons diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 071789fe16..31bb553809 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -118,11 +118,89 @@ Window { } HifiControls.Table { - tableModel: runningScriptsModel + model: runningScriptsModel + id: table height: 185 colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.right: parent.right + expandSelectedRow: true + + itemDelegate: Item { + anchors { + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + } + + FiraSansSemiBold { + id: textItem + text: styleData.value + size: hifi.fontSizes.tableText + color: table.colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 3 + } + + HiFiGlyphs { + id: reloadButton + text: hifi.glyphs.reloadSmall + color: reloadButtonArea.pressed ? hifi.colors.white : parent.color + anchors { + top: parent.top + right: stopButton.left + verticalCenter: parent.verticalCenter + } + MouseArea { + id: reloadButtonArea + anchors { fill: parent; margins: -2 } + onClicked: reloadScript(model.url) + } + } + + HiFiGlyphs { + id: stopButton + text: hifi.glyphs.closeSmall + color: stopButtonArea.pressed ? hifi.colors.white : parent.color + anchors { + top: parent.top + right: parent.right + verticalCenter: parent.verticalCenter + } + MouseArea { + id: stopButtonArea + anchors { fill: parent; margins: -2 } + onClicked: stopScript(model.url) + } + } + + } + + FiraSansSemiBold { + text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : "" + elide: Text.ElideMiddle + size: hifi.fontSizes.tableText + color: table.colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + top: textItem.bottom + left: parent.left + right: parent.right + } + visible: styleData.selected + } + } + + TableViewColumn { + role: "name" + } } HifiControls.VerticalSpacer { diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml new file mode 100644 index 0000000000..4ae0826772 --- /dev/null +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -0,0 +1,23 @@ +// +// FiraSansRegular.qml +// +// Created by David Rowe on 12 May 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Text { + id: root + FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; } + property real size: 32 + font.pixelSize: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + font.family: firaSansRegular.name +} diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cb4d2157ca..16f150b2d9 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -89,12 +89,16 @@ Item { readonly property color transparent: "#00ffffff" // Control specific colors - readonly property color tableRowLightOdd: white50 - readonly property color tableRowLightEven: "#1a575757" - readonly property color tableRowDarkOdd: "#80393939" - readonly property color tableRowDarkEven: "#a6181818" - readonly property color tableScrollHandle: "#707070" - readonly property color tableScrollBackground: "#323232" + readonly property color tableRowLightOdd: "#eaeaea" // Equivalent to white50 over #e3e3e3 background + readonly property color tableRowLightEven: "#c6c6c6" // Equivavlent to "#1a575757" over #e3e3e3 background + readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background + readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background + readonly property color tableBackgroundLight: tableRowLightEven + readonly property color tableBackgroundDark: tableRowDarkEven + readonly property color tableScrollHandleLight: tableRowLightOdd + readonly property color tableScrollHandleDark: "#707070" + readonly property color tableScrollBackgroundLight: tableRowLightEven + readonly property color tableScrollBackgroundDark: "#323232" readonly property color checkboxLightStart: "#ffffff" readonly property color checkboxLightFinish: "#afafaf" readonly property color checkboxDarkStart: "#7d7d7d" @@ -137,10 +141,10 @@ Item { readonly property real textPadding: 8 readonly property real sliderHandleSize: 18 readonly property real sliderGrooveHeight: 8 - readonly property real spinnerSize: 42 + readonly property real spinnerSize: 50 readonly property real tablePadding: 12 readonly property real tableRowHeight: largeScreen ? 26 : 23 - readonly property real tableHeaderHeight: 40 + readonly property real tableHeaderHeight: 29 readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30) readonly property real modalDialogTitleHeight: 40 readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor @@ -157,6 +161,8 @@ Item { readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12 readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9 readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24 + readonly property real tableHeading: dimensions.largeScreen ? 12 : 10 + readonly property real tableHeadingIcon: dimensions.largeScreen ? 40 : 33 readonly property real tableText: dimensions.largeScreen ? 15 : 12 readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9 readonly property real iconButton: dimensions.largeScreen ? 13 : 9 diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/edit-style.css index 5eaa3c6497..19d1cd95a9 100644 --- a/scripts/system/html/edit-style.css +++ b/scripts/system/html/edit-style.css @@ -268,7 +268,7 @@ input[type=number]::-webkit-inner-spin-button { height: 100%; overflow: hidden; font-family: hifi-glyphs; - font-size: 50px; + font-size: 46px; color: #afafaf; cursor: pointer; /*background-color: #000000;*/ @@ -276,17 +276,17 @@ input[type=number]::-webkit-inner-spin-button { input[type=number]::-webkit-inner-spin-button:before, input[type=number]::-webkit-inner-spin-button:after { position:absolute; - left: -21px; + left: -19px; line-height: 8px; text-align: center; } input[type=number]::-webkit-inner-spin-button:before { content: "6"; - top: 5px; + top: 4px; } input[type=number]::-webkit-inner-spin-button:after { content: "5"; - bottom: 6px; + bottom: 4px; } input[type=number].hover-up::-webkit-inner-spin-button:before, @@ -613,7 +613,7 @@ hr { margin-right: -48px; position: relative; left: -12px; - top: -11px; + top: -9px; } .dropdown dd { diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index e45749e1de..1745658193 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -211,6 +211,27 @@ ApplicationWindow { } } + Button { + text: "Open File" + property var builder: Component { + FileDialog { + title: "Open File" + filter: "All Files (*.*)" + //filter: "HTML files (*.html);;Other(*.png)" + } + } + + onClicked: { + var fileDialog = builder.createObject(desktop); + fileDialog.canceled.connect(function(){ + console.log("Cancelled") + }) + fileDialog.selectedFile.connect(function(file){ + console.log("Selected " + file) + }) + } + } + Button { text: "Add Tab" onClicked: { @@ -246,24 +267,7 @@ ApplicationWindow { } } - Button { - text: "Open File" - property var builder: Component { - FileDialog { } - } - - onClicked: { - var fileDialog = builder.createObject(desktop); - fileDialog.canceled.connect(function(){ - console.log("Cancelled") - }) - fileDialog.selectedFile.connect(function(file){ - console.log("Selected " + file) - }) - } - } } - /* Window { id: blue