From 61912b6ff0bced76e7405f509d9bb2cc59de30b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 9 Feb 2017 17:56:13 +0000 Subject: [PATCH 1/9] saving work --- scripts/system/generalSettings.js | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 scripts/system/generalSettings.js diff --git a/scripts/system/generalSettings.js b/scripts/system/generalSettings.js new file mode 100644 index 0000000000..5c658951b9 --- /dev/null +++ b/scripts/system/generalSettings.js @@ -0,0 +1,57 @@ +"use strict"; + +// +// generalSettings.js +// scripts/system/ +// +// Created by Dante Ruiz on 9 Feb 2017 +// 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 +// +/* globals Tablet, Toolbars, Script, HMD, DialogsManager */ + +(function() { // BEGIN LOCAL_SCOPE + + var button; + var buttonName = "Settings"; + var toolBar = null; + var tablet = null; + var settings = "../../windows/TabletScrollingWindow.qml" + function onClicked(){ + if (tablet) { + tablet.loadQMLSource(settings); + } + } + + if (Settings.getValue("HUDUIEnabled")) { + toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + button = toolBar.addButton({ + objectName: buttonName, + imageURL: Script.resolvePath("assets/images/tools/directory.svg"), + visible: true, + alpha: 0.9 + }); + } else { + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + button = tablet.addButton({ + icon: "icons/tablet-icons/goto-i.svg", + text: buttonName, + sortOrder: 8 + }); + } + + button.clicked.connect(onClicked); + + Script.scriptEnding.connect(function () { + button.clicked.disconnect(onClicked); + if (tablet) { + tablet.removeButton(button); + } + if (toolBar) { + toolBar.removeButton(buttonName); + } + }); + +}()); // END LOCAL_SCOPE From 46cfa7cf1394a968703970bf33c25f887ab2b8cd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 10 Feb 2017 16:50:52 +0000 Subject: [PATCH 2/9] saving work --- .../qml/controls-uit/ContentSection.qml | 8 +- .../qml/hifi/tablet/TabletGeneralSettings.qml | 23 + .../hifi/tablet/tabletWindows/FileDialog.qml | 783 ++++++++++++++++++ .../tabletWindows/TabletPreferencesDialog.qml | 85 ++ .../tabletWindows/TabletScrollingWindow.qml | 172 ++++ .../tabletWindows/preferences/Preference.qml | 28 + .../tabletWindows/preferences/Section.qml | 140 ++++ .../preferences/TabletBrowsablePreference.qml | 79 ++ interface/src/ui/overlays/Web3DOverlay.cpp | 2 + scripts/system/generalSettings.js | 2 +- 10 files changed, 1317 insertions(+), 5 deletions(-) create mode 100644 interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml index 47a13e9262..012b803738 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -58,14 +58,14 @@ Column { Rectangle { id: shadow - width: frame.width + width: 480//frame ? frame.width: 480 height: 1 color: hifi.colors.baseGrayShadow x: -hifi.dimensions.contentMargin.x } Rectangle { - width: frame.width + width: 480 //frame ? frame.width : 480 height: 1 color: hifi.colors.baseGrayHighlight x: -hifi.dimensions.contentMargin.x @@ -121,8 +121,8 @@ Column { LinearGradient { id: bottomBar - visible: desktop.gradientsSupported && isCollapsible - width: frame.width + visible: false //(desktop ? desktop.gradientsSupported : false) && isCollapsible + width: 480 //(frame ? frame.width : 480) height: visible ? 4 : 0 x: -hifi.dimensions.contentMargin.x anchors.top: heading.bottom diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml b/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml new file mode 100644 index 0000000000..88e3403e2d --- /dev/null +++ b/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml @@ -0,0 +1,23 @@ +// +// TabletGeneralSettings.qml +// scripts/system/ +// +// Created by Dante Ruiz on 9 Feb 2017 +// 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 "tabletWindows" +import "../../dialogs" + +TabletPreferencesDialog { + id: root + objectName: "GeneralPreferencesDialog" + width: parent.width + height: parent.height + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] + +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml new file mode 100644 index 0000000000..0886a25949 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml @@ -0,0 +1,783 @@ +// +// 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 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import ".." +import "../controls-uit" +import "../styles-uit" +import "../windows" + +import "fileDialog" + +//FIXME implement shortcuts for favorite location +ModalWindow { + id: root + resizable: true + implicitWidth: 480 + implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0) + + minSize: Qt.vector2d(360, 240) + draggable: true + + HifiConstants { id: hifi } + + Settings { + category: "FileDialog" + property alias width: root.width + property alias height: root.height + property alias x: root.x + property alias y: root.y + } + + + // Set from OffscreenUi::getOpenFile() + property alias caption: root.title; + // Set from OffscreenUi::getOpenFile() + 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: root.title !== "" ? hifi.glyphs.scriptUpload : "" + property int iconSize: 40 + + property bool selectDirectory: false; + property bool showHidden: false; + // FIXME implement + property bool multiSelect: false; + property bool saveDialog: false; + property var helper: fileDialogHelper + 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); + + fileDialogItem.keyboardEnabled = HMD.active; + + // 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 : ""; + + // Clear selection when click on external frame. + frameClicked.connect(function() { d.clearSelection(); }); + + if (selectDirectory) { + currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder)); + d.currentSelectionIsFolder = true; + d.currentSelectionUrl = initialFolder; + } + + helper.contentsChanged.connect(function() { + if (folderListModel) { + // Make folderListModel refresh. + var save = folderListModel.folder; + folderListModel.folder = ""; + folderListModel.folder = save; + } + }); + + fileTableView.forceActiveFocus(); + } + + Item { + id: fileDialogItem + clip: true + width: pane.width + height: pane.height + anchors.margins: 0 + + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + + MouseArea { + // Clear selection when click on internal unused area. + anchors.fill: parent + drag.target: root + onClicked: { + d.clearSelection(); + frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden. + } + } + + Row { + id: navControls + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: parent.left + } + spacing: hifi.dimensions.contentSpacing.x + + GlyphButton { + id: upButton + glyph: hifi.glyphs.levelUp + width: height + size: 30 + enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" + onClicked: d.navigateUp(); + } + + GlyphButton { + id: homeButton + property var destination: helper.home(); + glyph: hifi.glyphs.home + size: 28 + width: height + enabled: d.homeDestination ? true : false + onClicked: d.navigateHome(); + } + } + + ComboBox { + id: pathSelector + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: navControls.right + leftMargin: hifi.dimensions.contentSpacing.x + right: parent.right + } + + 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()) { + if (root.selectDirectory) { + currentSelection.text = currentText !== "This PC" ? currentText : ""; + d.currentSelectionUrl = helper.pathToUrl(currentText); + } + fileTableModel.folder = folder; + fileTableView.forceActiveFocus(); + } + } + } + + QtObject { + id: d + property var currentSelectionUrl; + readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl); + property bool currentSelectionIsFolder; + property var backStack: [] + property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } + property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } + property var homeDestination: helper.home(); + + 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) { + if (!root.selectDirectory) { + currentSelection.text = ""; + currentSelectionIsFolder = false; + } + return; + } + + currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); + currentSelectionIsFolder = fileTableView.model.isFolder(row); + if (root.selectDirectory || !currentSelectionIsFolder) { + currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); + } else { + currentSelection.text = ""; + } + } + + function navigateUp() { + if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { + fileTableModel.folder = fileTableModel.parentFolder; + return true; + } + } + + function navigateHome() { + fileTableModel.folder = homeDestination; + return true; + } + + function clearSelection() { + fileTableView.selection.clear(); + fileTableView.currentRow = -1; + update(); + } + } + + 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; + helper.monitorDirectory(""); + update(); + } else { + var needsUpdate = model === driveListModel && folder === folderListModel.folder; + + model = folderListModel; + folderListModel.folder = folder; + helper.monitorDirectory(helper.urlToPath(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++; + } + + d.clearSelection(); + } + } + + Table { + id: fileTableView + 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(); + + sortIndicatorColumn: 0 + sortIndicatorOrder: Qt.AscendingOrder + sortIndicatorVisible: true + + model: fileTableModel + + function updateSort() { + model.sortOrder = sortIndicatorOrder; + model.sortColumn = sortIndicatorColumn; + model.update(); + } + + onSortIndicatorColumnChanged: { updateSort(); } + + onSortIndicatorOrderChanged: { updateSort(); } + + 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(); + } + + function navigateToCurrentRow() { + var row = fileTableView.currentRow + var isFolder = model.isFolder(row); + var file = model.get(row).filePath; + if (isFolder) { + fileTableView.model.folder = helper.pathToUrl(file); + } else { + okAction.trigger(); + } + } + + property string prefix: "" + + function addToPrefix(event) { + if (!event.text || event.text === "") { + return false; + } + var newPrefix = prefix + event.text.toLowerCase(); + var matchedIndex = -1; + for (var i = 0; i < model.count; ++i) { + var name = model.get(i).fileName.toLowerCase(); + if (0 === name.indexOf(newPrefix)) { + matchedIndex = i; + break; + } + } + + if (matchedIndex !== -1) { + fileTableView.selection.clear(); + fileTableView.selection.select(matchedIndex); + fileTableView.currentRow = matchedIndex; + fileTableView.prefix = newPrefix; + } + prefixClearTimer.restart(); + return true; + } + + Timer { + id: prefixClearTimer + interval: 1000 + repeat: false + running: false + onTriggered: fileTableView.prefix = ""; + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Backspace: + case Qt.Key_Tab: + case Qt.Key_Backtab: + event.accepted = false; + break; + + default: + if (addToPrefix(event)) { + event.accepted = true + } else { + event.accepted = false; + } + break; + } + } + } + + TextField { + id: currentSelection + label: selectDirectory ? "Directory:" : "File name:" + anchors { + left: parent.left + right: selectionType.visible ? selectionType.left: parent.right + rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 + bottom: keyboard.top + bottomMargin: hifi.dimensions.contentSpacing.y + } + readOnly: !root.saveDialog + activeFocusOnTab: !readOnly + onActiveFocusChanged: if (activeFocus) { selectAll(); } + onAccepted: okAction.trigger(); + } + + FileTypeSelection { + id: selectionType + anchors { + top: currentSelection.top + left: buttonRow.left + right: parent.right + } + visible: !selectDirectory && filtersCount > 1 + KeyNavigation.left: fileTableView + KeyNavigation.right: openButton + } + + Keyboard { + id: keyboard + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: buttonRow.top + bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0 + } + } + + Row { + id: buttonRow + 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 + KeyNavigation.up: selectionType + KeyNavigation.left: openButton + KeyNavigation.right: fileTableView.contentItem + Keys.onReturnPressed: { canceled(); root.enabled = false } + } + } + + Action { + id: okAction + text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open" + enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false + onTriggered: { + if (!root.selectDirectory && !d.currentSelectionIsFolder + || root.selectDirectory && fileTableView.currentRow === -1) { + okActionTimer.start(); + } else { + fileTableView.navigateToCurrentRow(); + } + } + } + + Timer { + id: okActionTimer + interval: 50 + running: false + repeat: false + onTriggered: { + if (!root.saveDialog) { + selectedFile(d.currentSelectionUrl); + root.destroy() + return; + } + + // Handle the ambiguity between different cases + // * typed name (with or without extension) + // * full path vs relative vs filename only + var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); + + if (!selection) { + desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) + return; + } + + if (helper.urlIsDir(selection)) { + root.dir = selection; + currentSelection.text = ""; + return; + } + + // Check if the file is a valid target + if (!helper.urlIsWritable(selection)) { + desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Warning, + text: "Unable to write to location " + selection + }) + return; + } + + if (helper.urlExists(selection)) { + var messageBox = desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Do you wish to overwrite " + selection + "?", + }); + var result = messageBox.exec(); + if (OriginalDialogs.StandardButton.Yes !== result) { + return; + } + } + + console.log("Selecting " + selection) + selectedFile(selection); + root.destroy(); + } + } + + Action { + id: cancelAction + text: "Cancel" + onTriggered: { canceled(); root.shown = false; } + } + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Backspace: + event.accepted = d.navigateUp(); + break; + + case Qt.Key_Home: + event.accepted = d.navigateHome(); + break; + + } + } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml new file mode 100644 index 0000000000..51441381fd --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -0,0 +1,85 @@ +// +// TabletPreferencesDialog.qml +// +// Created by Dante Ruiz on 9 Feb 2017 +// 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 +import QtGraphicalEffects 1.0 + +import "." +import "./preferences" +import "../../../styles-uit" +import "../../../controls-uit" as HifiControls + +Item { + id: root + width: 480 + height: 600 + + HifiConstants { id: hifi } + property var sections: [] + property var showCategories: [] + + function saveAll() { + + } + + function restoreAll() { + + } + + ListView { + + Component { + id: sectionBuilder + Section {} + } + + Component.onCompleted: { + var categories = Preferences.categories; + var i; + + // build a map of valid categories. + var categoryMap = {}; + for (i = 0; i < categories.length; i++) { + categoryMap[categories[i]] = true; + } + + // create a section for each valid category in showCategories + // NOTE: the sort order of items in the showCategories array is the same order in the dialog. + for (i = 0; i < showCategories.length; i++) { + if (categoryMap[showCategories[i]]) { + sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]})); + } + } + + if (sections.length) { + // Default sections to expanded/collapsed as appropriate for dialog. + if (sections.length === 1) { + sections[0].collapsable = false + sections[0].expanded = true + } else { + for (i = 0; i < sections.length; i++) { + sections[i].collapsable = true; + sections[i].expanded = true; + } + } + sections[0].isFirst = true; + sections[sections.length - 1].isLast = true; + } + } + + Column { + id: prefControls + width: 320 + } + } + +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml new file mode 100644 index 0000000000..416842e204 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml @@ -0,0 +1,172 @@ +// +// TabletsScrollingWindow.qml +// +// Created by Dante Ruiz on 9 Feb 2017 +// 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 +import QtGraphicalEffects 1.0 + +import "." +import "../../../styles-uit" +import "../../../controls-uit" as HiFiControls +FocusScope{ + id: window + HifiConstants { id: hifi } + + childern [ plane ] + property var footer: Item {} + readonly var footerContentHeight: 40 + property var pane: Item { + property bool isScrolling: true//scrollView.height < scrollView.contentItem.height + property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) + property int scrollHeight: scrollView.height + anchors.fill: parent + anchors.rightMargin: 11 + Rectangle { + id: contentBackground + anchors.fill: parent + color: hifi.colors.baseGray + visible: true + } + + LinearGradient { + visible: true + anchors.top: contentBackground.bottom + anchors.left: contentBackground.left + width: contentBackground.width - 1 + height: 4 + start: Qt.point(0, 0) + end: Qt.point(0, 4) + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.darkGray } + GradientStop { position: 1.0; color: hifi.colors.darkGray0 } + } + cached: true + } + + ScrollView { + id: scrollView + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + verticalScrollBarPolicy: Qt.ScrollBarAsNeeded + anchors.fill: parent + anchors.rightMargin: parent.isScrolling ? 1 : 0 + anchors.bottomMargin: footerPane.height + + style: ScrollViewStyle { + + padding.right: -7 // Move to right away from content. + + handle: Item { + implicitWidth: 8 + Rectangle { + radius: 4 + color: hifi.colors.white30 + anchors { + fill: parent + leftMargin: 2 // Finesse size and position. + topMargin: 1 + bottomMargin: 1 + } + } + } + + scrollBarBackground: Item { + implicitWidth: 10 + Rectangle { + color: hifi.colors.darkGray30 + radius: 4 + anchors { + fill: parent + topMargin: -1 // Finesse size + bottomMargin: -2 + } + } + } + + incrementControl: Item { + visible: false + } + + decrementControl: Item { + visible: false + } + } + } + + function scrollBy(delta) { + scrollView.flickableItem.contentY += delta; + } + + Rectangle { + // Optional non-scrolling footer. + id: footerPane + + property alias keyboardOverride: window.keyboardOverride + property alias keyboardRaised: window.keyboardRaised + property alias punctuationMode: window.punctuationMode + + anchors { + left: parent.left + bottom: parent.bottom + } + width: parent.contentWidth + height: footerContentHeight + (keyboard.enabled && keyboard.raised ? keyboard.height : 0) + color: hifi.colors.baseGray + visible: footer.height > 0 || keyboard.enabled && keyboard.raised + + Item { + // Horizontal rule. + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + visible: footer.height > 0 + + Rectangle { + width: parent.width + height: 1 + y: 1 // Stop displaying content just above horizontal rule/=. + color: hifi.colors.baseGrayShadow + } + + Rectangle { + width: parent.width + height: 1 + y: 2 + color: hifi.colors.baseGrayHighlight + } + } + + Item { + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: hifi.dimensions.contentSpacing.y + 3 + } + children: [ footer ] + } + + HiFiControls.Keyboard { + id: keyboard + enabled: !keyboardOverride + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + } + } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml new file mode 100644 index 0000000000..1d72197382 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml @@ -0,0 +1,28 @@ +// +// Preference.qml +// +// Created by Bradley Austin Davis on 18 Jan 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 + +Item { + id: root + anchors { left: parent.left; right: parent.right } + property var preference; + property string label: preference ? preference.name : ""; + property bool isFirstCheckBox; + Component.onCompleted: { + if (preference) { + preference.load(); + enabled = Qt.binding(function() { return preference.enabled; } ); + } + } + + function restore() { } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml new file mode 100644 index 0000000000..eca4ea0c7b --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -0,0 +1,140 @@ +// +// Section.qml +// +// Created by Bradley Austin Davis on 18 Jan 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 Hifi 1.0 + +import "../../../../dialogs/preferences" +import "../../../../controls-uit" as HiFiControls +import "../../../../styles-uit" +import "." + +Preference { + id: root + property bool collapsable: true + property bool expanded: false + property bool isFirst: false + property bool isLast: false + property string name: "Header" + property real spacing: 8 + default property alias preferences: contentContainer.children + + HifiConstants { id: hifi } + + function saveAll() { + for (var i = 0; i < d.preferences.length; ++i) { + var preference = d.preferences[i]; + preference.save(); + } + } + + function restoreAll() { + for (var i = 0; i < d.preferences.length; ++i) { + var preference = d.preferences[i]; + preference.restore(); + } + } + + children: [ contentContainer ] + + height: contentContainer.height + (contentContainer.isCollapsed ? 0 : hifi.dimensions.controlInterlineHeight) + + Component.onCompleted: d.buildPreferences(); + + HiFiControls.ContentSection { + id: contentContainer + name: root.name + isFirst: root.isFirst + isCollapsible: root.collapsable + isCollapsed: !root.expanded + + anchors { + left: parent.left + right: parent.right + margins: 0 + } + } + + QtObject { + id: d + property var editableBuilder: Component { EditablePreference { } } + property var browsableBuilder: Component { TabletBrowsablePreference { } } + property var spinnerBuilder: Component { SpinBoxPreference { } } + property var checkboxBuilder: Component { CheckBoxPreference { } } + property var sliderBuilder: Component { SliderPreference { } } + property var avatarBuilder: Component { AvatarPreference { } } + property var buttonBuilder: Component { ButtonPreference { } } + property var comboBoxBuilder: Component { ComboBoxPreference { } } + property var preferences: [] + property int checkBoxCount: 0 + + function buildPreferences() { + var categoryPreferences = Preferences.preferencesByCategory[root.name]; + if (categoryPreferences) { + console.log("Category " + root.name + " with " + categoryPreferences.length + " preferences"); + for (var j = 0; j < categoryPreferences.length; ++j) { + buildPreference(categoryPreferences[j]); + } + } + } + + function buildPreference(preference) { + console.log("\tPreference type " + preference.type + " name " + preference.name) + var builder; + switch (preference.type) { + case Preference.Editable: + checkBoxCount = 0; + builder = editableBuilder; + break; + + case Preference.Browsable: + checkBoxCount = 0; + builder = browsableBuilder; + break; + + case Preference.Spinner: + checkBoxCount = 0; + builder = spinnerBuilder; + break; + + case Preference.Slider: + checkBoxCount = 0; + builder = sliderBuilder; + break; + + case Preference.Checkbox: + checkBoxCount++; + builder = checkboxBuilder; + break; + + case Preference.Avatar: + checkBoxCount = 0; + builder = avatarBuilder; + break; + + case Preference.Button: + checkBoxCount = 0; + builder = buttonBuilder; + break; + + case Preference.ComboBox: + checkBoxCount = 0; + builder = comboBoxBuilder; + break; + }; + + if (builder) { + preferences.push(builder.createObject(contentContainer, { preference: preference, isFirstCheckBox: (checkBoxCount === 1) })); + } + } + } +} + diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml new file mode 100644 index 0000000000..cbfc83c2bc --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -0,0 +1,79 @@ +// +// BrowsablePreference.qml +// +// Created by Bradley Austin Davis on 18 Jan 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 "../../../../dialogs" +import "../../../../controls-uit" + +Preference { + id: root + property alias text: dataTextField.text + property alias placeholderText: dataTextField.placeholderText + height: control.height + hifi.dimensions.controlInterlineHeight + + Component.onCompleted: { + dataTextField.text = preference.value; + } + + function save() { + preference.value = dataTextField.text; + preference.save(); + } + + Item { + id: control + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: Math.max(dataTextField.controlHeight, button.height) + + TextField { + id: dataTextField + + anchors { + left: parent.left + right: button.left + rightMargin: hifi.dimensions.contentSpacing.x + bottom: parent.bottom + } + + label: root.label + placeholderText: root.placeholderText + colorScheme: hifi.colorSchemes.dark + } + + Component { + id: fileBrowserBuilder; + FileDialog { selectDirectory: true } + } + + Button { + id: button + text: preference.browseLabel + anchors { + right: parent.right + verticalCenter: dataTextField.verticalCenter + } + onClicked: { + var browser = fileBrowserBuilder.createObject(desktop, { + selectDirectory: true, + dir: fileDialogHelper.pathToUrl(preference.value) + }); + browser.selectedFile.connect(function(fileUrl){ + console.log(fileUrl); + dataTextField.text = fileDialogHelper.urlToPath(fileUrl); + }); + } + } + } +} diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ddf380d0b2..c198ca2e4e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -37,6 +37,7 @@ #include #include "scripting/AccountScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" +#include static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -158,6 +159,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("Users", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("Preferences", DependencyManager::get().data()); if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); diff --git a/scripts/system/generalSettings.js b/scripts/system/generalSettings.js index 5c658951b9..0a9fc823ae 100644 --- a/scripts/system/generalSettings.js +++ b/scripts/system/generalSettings.js @@ -18,7 +18,7 @@ var buttonName = "Settings"; var toolBar = null; var tablet = null; - var settings = "../../windows/TabletScrollingWindow.qml" + var settings = "TabletGeneralSettings.qml" function onClicked(){ if (tablet) { tablet.loadQMLSource(settings); From 888d4ff706119e518811bed4328e89619c43be91 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Sat, 11 Feb 2017 00:51:01 +0000 Subject: [PATCH 3/9] basic prefreneces done --- .../qml/hifi/tablet/TabletGeneralSettings.qml | 35 +++- .../tabletWindows/TabletPreferencesDialog.qml | 180 +++++++++++++----- .../tabletWindows/TabletScrollingWindow.qml | 159 ++-------------- .../tabletWindows/preferences/Section.qml | 2 +- .../preferences/TabletBrowsablePreference.qml | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 + 6 files changed, 186 insertions(+), 194 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml b/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml index 88e3403e2d..b445e6a463 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralSettings.qml @@ -12,12 +12,33 @@ import QtQuick 2.5 import "tabletWindows" import "../../dialogs" +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtGraphicalEffects 1.0 + +StackView { + id: profileRoot + initialItem: root + objectName: "stack" + + property var eventBridge; + signal sendToScript(var message); + + function pushSource(path) { + editRoot.push(Qt.reslovedUrl(path)); + } + + function popSource() { + + } + + TabletPreferencesDialog { + id: root + objectName: "GeneralPreferencesDialog" + width: parent.width + height: parent.height + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] + + } -TabletPreferencesDialog { - id: root - objectName: "GeneralPreferencesDialog" - width: parent.width - height: parent.height - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] - } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 51441381fd..7a890a452b 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -13,72 +13,166 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 + import "." import "./preferences" import "../../../styles-uit" import "../../../controls-uit" as HifiControls Item { - id: root + id: dialog width: 480 - height: 600 + height: 720 HifiConstants { id: hifi } property var sections: [] property var showCategories: [] - + function saveAll() { - + for (var i = 0; i < sections.length; ++i) { + var section = sections[i]; + section.saveAll(); + } } function restoreAll() { - + for (var i = 0; i < sections.length; ++i) { + var section = sections[i]; + section.restoreAll(); + } + } + + Rectangle { + id: main + height: parent.height - 40 + anchors { + top: parent.top + bottom: footer.top + left: parent.left + right: parent.right + } + gradient: Gradient { + GradientStop { + position: 0 + color: "#2b2b2b" + + } + + GradientStop { + position: 1 + color: "#0f212e" + } + } + Flickable { + id: scrollView + width: parent.width + height: parent.height + contentWidth: parent.width + contentHeight: getSectionsHeight(); + Column { + width: 480 + Component { + id: sectionBuilder + Section {} + } + + Component.onCompleted: { + var categories = Preferences.categories; + var i; + + // build a map of valid categories. + var categoryMap = {}; + for (i = 0; i < categories.length; i++) { + categoryMap[categories[i]] = true; + } + + // create a section for each valid category in showCategories + // NOTE: the sort order of items in the showCategories array is the same order in the dialog. + for (i = 0; i < showCategories.length; i++) { + if (categoryMap[showCategories[i]]) { + sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]})); + } + } + + if (sections.length) { + // Default sections to expanded/collapsed as appropriate for dialog. + if (sections.length === 1) { + sections[0].collapsable = false + sections[0].expanded = true + } else { + for (i = 0; i < sections.length; i++) { + sections[i].collapsable = false; + sections[i].expanded = true; + } + } + sections[0].isFirst = true; + sections[sections.length - 1].isLast = true; + } + + scrollView.contentHeight = scrollView.getSectionsHeight(); + + } + + + Column { + id: prefControls + width: 480 + } + } + + function getSectionsHeight() { + var totalHeight = 0; + for (var i = 0; i < sections.length; i++) { + totalHeight += sections[i].height + } + console.log(totalHeight); + return totalHeight; + } + } } - ListView { + Rectangle { + id: footer + height: 40 - Component { - id: sectionBuilder - Section {} + anchors { + top: main.bottom + bottom: parent.bottom + left: parent.left + right: parent.right } - - Component.onCompleted: { - var categories = Preferences.categories; - var i; - - // build a map of valid categories. - var categoryMap = {}; - for (i = 0; i < categories.length; i++) { - categoryMap[categories[i]] = true; + gradient: Gradient { + GradientStop { + position: 0 + color: "#2b2b2b" + } - - // create a section for each valid category in showCategories - // NOTE: the sort order of items in the showCategories array is the same order in the dialog. - for (i = 0; i < showCategories.length; i++) { - if (categoryMap[showCategories[i]]) { - sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]})); - } - } - - if (sections.length) { - // Default sections to expanded/collapsed as appropriate for dialog. - if (sections.length === 1) { - sections[0].collapsable = false - sections[0].expanded = true - } else { - for (i = 0; i < sections.length; i++) { - sections[i].collapsable = true; - sections[i].expanded = true; - } - } - sections[0].isFirst = true; - sections[sections.length - 1].isLast = true; + + GradientStop { + position: 1 + color: "#0f212e" } } - Column { - id: prefControls - width: 320 + Row { + anchors { + top: parent,top + right: parent.right + rightMargin: hifi.dimensions.contentMargin.x + } + + spacing: hifi.dimensions.contentSpacing.x + HifiControls.Button { + text: "Save changes" + color: hifi.buttons.blue + onClicked: root.saveAll() + } + + HifiControls.Button { + text: "Cancel" + color: hifi.buttons.white + onClicked: root.restoreAll() + } } } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml index 416842e204..5b43d20109 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml @@ -16,157 +16,32 @@ import QtGraphicalEffects 1.0 import "." import "../../../styles-uit" import "../../../controls-uit" as HiFiControls -FocusScope{ +Item { + id: window HifiConstants { id: hifi } - childern [ plane ] + children: [ pane ] property var footer: Item {} - readonly var footerContentHeight: 40 property var pane: Item { - property bool isScrolling: true//scrollView.height < scrollView.contentItem.height - property int contentWidth: scrollView.width - (isScrolling ? 10 : 0) - property int scrollHeight: scrollView.height anchors.fill: parent - anchors.rightMargin: 11 + Rectangle { id: contentBackground anchors.fill: parent - color: hifi.colors.baseGray - visible: true - } - - LinearGradient { - visible: true - anchors.top: contentBackground.bottom - anchors.left: contentBackground.left - width: contentBackground.width - 1 - height: 4 - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: hifi.colors.darkGray } - GradientStop { position: 1.0; color: hifi.colors.darkGray0 } - } - cached: true - } - - ScrollView { - id: scrollView - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: Qt.ScrollBarAsNeeded - anchors.fill: parent - anchors.rightMargin: parent.isScrolling ? 1 : 0 - anchors.bottomMargin: footerPane.height - - style: ScrollViewStyle { - - padding.right: -7 // Move to right away from content. - - handle: Item { - implicitWidth: 8 - Rectangle { - radius: 4 - color: hifi.colors.white30 - anchors { - fill: parent - leftMargin: 2 // Finesse size and position. - topMargin: 1 - bottomMargin: 1 - } - } - } - - scrollBarBackground: Item { - implicitWidth: 10 - Rectangle { - color: hifi.colors.darkGray30 - radius: 4 - anchors { - fill: parent - topMargin: -1 // Finesse size - bottomMargin: -2 - } - } - } - - incrementControl: Item { - visible: false - } - - decrementControl: Item { - visible: false - } - } - } - - function scrollBy(delta) { - scrollView.flickableItem.contentY += delta; - } - - Rectangle { - // Optional non-scrolling footer. - id: footerPane - - property alias keyboardOverride: window.keyboardOverride - property alias keyboardRaised: window.keyboardRaised - property alias punctuationMode: window.punctuationMode - - anchors { - left: parent.left - bottom: parent.bottom - } - width: parent.contentWidth - height: footerContentHeight + (keyboard.enabled && keyboard.raised ? keyboard.height : 0) - color: hifi.colors.baseGray - visible: footer.height > 0 || keyboard.enabled && keyboard.raised - - Item { - // Horizontal rule. - anchors { - top: parent.top - left: parent.left - right: parent.right - } - - visible: footer.height > 0 - - Rectangle { - width: parent.width - height: 1 - y: 1 // Stop displaying content just above horizontal rule/=. - color: hifi.colors.baseGrayShadow - } - - Rectangle { - width: parent.width - height: 1 - y: 2 - color: hifi.colors.baseGrayHighlight - } - } - - Item { - anchors { - left: parent.left - right: parent.right - top: parent.top - topMargin: hifi.dimensions.contentSpacing.y + 3 - } - children: [ footer ] - } - - HiFiControls.Keyboard { - id: keyboard - enabled: !keyboardOverride - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } + width: 480 + //gradient: Gradient { + //GradientStop { + //position: 0 + //color: "#2b2b2b" + + //} + + //GradientStop { + //position: 1 + //color: "#0f212e" + //} + //} } } } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index eca4ea0c7b..36cedbdfd3 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -19,7 +19,7 @@ import "." Preference { id: root - property bool collapsable: true + property bool collapsable: false property bool expanded: false property bool isFirst: false property bool isLast: false diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml index cbfc83c2bc..06880b22cf 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -65,7 +65,7 @@ Preference { verticalCenter: dataTextField.verticalCenter } onClicked: { - var browser = fileBrowserBuilder.createObject(desktop, { + var browser = fileBrowserBuilder.createObject({ selectDirectory: true, dir: fileDialogHelper.pathToUrl(preference.value) }); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index c198ca2e4e..cb649e8766 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -38,6 +38,7 @@ #include "scripting/AccountScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include +#include "FileDialogHelper.h" static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -168,6 +169,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("AddressManager", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("Account", AccountScriptingInterface::getInstance()); _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); // Override min fps for tablet UI, for silky smooth scrolling From 948352cdb682d6d415865bd2bac8e1893fc31e1a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 13 Feb 2017 23:42:04 +0000 Subject: [PATCH 4/9] general preferences work on tablet --- .../qml/controls-uit/TabletComboBox.qml | 211 +++++ .../tablet/tabletWindows/TabletFileDialog.qml | 775 ++++++++++++++++++ .../tabletWindows/TabletPreferencesDialog.qml | 2 +- .../tabletWindows/preferences/Section.qml | 8 + .../preferences/TabletBrowsablePreference.qml | 6 +- 5 files changed, 1000 insertions(+), 2 deletions(-) create mode 100644 interface/resources/qml/controls-uit/TabletComboBox.qml create mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml diff --git a/interface/resources/qml/controls-uit/TabletComboBox.qml b/interface/resources/qml/controls-uit/TabletComboBox.qml new file mode 100644 index 0000000000..cd26494ef1 --- /dev/null +++ b/interface/resources/qml/controls-uit/TabletComboBox.qml @@ -0,0 +1,211 @@ +// +// ComboBox.qml +// +// Created by Bradley Austin David on 27 Jan 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 + +import "../styles-uit" +import "../controls-uit" as HifiControls +import "." as VrControls + +FocusScope { + id: root + HifiConstants { id: hifi } + + property alias model: comboBox.model; + property alias comboBox: comboBox + readonly property alias currentText: comboBox.currentText; + property alias currentIndex: comboBox.currentIndex; + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property string label: "" + property real controlHeight: height + (comboBoxLabel.visible ? comboBoxLabel.height + comboBoxLabel.anchors.bottomMargin : 0) + + readonly property ComboBox control: comboBox + + signal accepted(); + + implicitHeight: comboBox.height; + focus: true + + Rectangle { + id: background + gradient: Gradient { + GradientStop { + position: 0.2 + color: popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightStart : hifi.colors.dropDownDarkStart) + } + GradientStop { + position: 1.0 + color: popup.visible + ? (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + : (isLightColorScheme ? hifi.colors.dropDownLightFinish : hifi.colors.dropDownDarkFinish) + } + } + anchors.fill: parent + } + + SystemPalette { id: palette } + + ComboBox { + id: comboBox + anchors.fill: parent + visible: false + height: hifi.fontSizes.textFieldInput + 13 // Match height of TextField control. + } + + FiraSansSemiBold { + id: textField + anchors { + left: parent.left + leftMargin: hifi.dimensions.textPadding + right: dropIcon.left + verticalCenter: parent.verticalCenter + } + size: hifi.fontSizes.textFieldInput + text: comboBox.currentText + elide: Text.ElideRight + color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) + } + + Item { + id: dropIcon + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + height: background.height + width: height + Rectangle { + width: 1 + height: parent.height + anchors.top: parent.top + anchors.left: parent.left + color: isLightColorScheme ? hifi.colors.faintGray : hifi.colors.baseGray + } + HiFiGlyphs { + anchors { + top: parent.top + topMargin: -11 + horizontalCenter: parent.horizontalCenter + } + size: hifi.dimensions.spinnerSize + text: hifi.glyphs.caratDn + color: controlHover.containsMouse || popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText) + } + } + + MouseArea { + id: controlHover + hoverEnabled: true + anchors.fill: parent + onClicked: toggleList(); + } + + function toggleList() { + if (popup.visible) { + hideList(); + } else { + showList(); + } + } + + function showList() { + var r = 20//desktop.mapFromItem(root, 0, 0, root.width, root.height); + var y = 200; + var bottom = 0 + scrollView.height; + if (bottom > 720) { + y -= bottom - 720 + 8; + } + scrollView.x = 0; + scrollView.y = 0; + popup.visible = true; + popup.forceActiveFocus(); + listView.currentIndex = root.currentIndex; + scrollView.hoverEnabled = true; + } + + function hideList() { + popup.visible = false; + scrollView.hoverEnabled = false; + root.accepted(); + } + + FocusScope { + id: popup + parent: parent + anchors.fill: parent + visible: false + focus: true + + MouseArea { + anchors.fill: parent + onClicked: hideList(); + } + + function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; } + function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; } + function selectCurrentItem() { root.currentIndex = listView.currentIndex; hideList(); } + function selectSpecificItem(index) { root.currentIndex = index; hideList(); } + + Keys.onUpPressed: previousItem(); + Keys.onDownPressed: nextItem(); + Keys.onSpacePressed: selectCurrentItem(); + Keys.onRightPressed: selectCurrentItem(); + Keys.onReturnPressed: selectCurrentItem(); + Keys.onEscapePressed: hideList(); + + ScrollView { + id: scrollView + height: 480 + width: root.width + 4 + property bool hoverEnabled: false; + + ListView { + id: listView + height: textField.height * count * 1.4 + model: root.model + delegate: Rectangle { + width: root.width + 4 + height: popupText.implicitHeight * 1.4 + color: (listView.currentIndex === index) ? hifi.colors.primaryHighlight : + (isLightColorScheme ? hifi.colors.dropDownPressedLight : hifi.colors.dropDownPressedDark) + FiraSansSemiBold { + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.textPadding + anchors.verticalCenter: parent.verticalCenter + id: popupText + text: listView.model[index] ? listView.model[index] : "" + size: hifi.fontSizes.textFieldInput + color: hifi.colors.baseGray + } + MouseArea { + id: popupHover + anchors.fill: parent; + hoverEnabled: scrollView.hoverEnabled; + onEntered: listView.currentIndex = index; + onClicked: popup.selectSpecificItem(index); + } + } + } + } + } + + HifiControls.Label { + id: comboBoxLabel + text: root.label + colorScheme: root.colorScheme + anchors.left: parent.left + anchors.bottom: parent.top + anchors.bottomMargin: 4 + visible: label != "" + } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml new file mode 100644 index 0000000000..205a93a935 --- /dev/null +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -0,0 +1,775 @@ +// +// 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 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs + +import ".." +import "../../../controls-uit" +import "../../../styles-uit" +import "../../../windows" + +import "../../../dialogs/fileDialog" + +//FIXME implement shortcuts for favorite location +Item { + id: root + anchors.top: parent.top + HifiConstants { id: hifi } + + Settings { + category: "FileDialog" + property alias width: root.width + property alias height: root.height + property alias x: root.x + property alias y: root.y + } + + + // Set from OffscreenUi::getOpenFile() + // property alias caption: root.title; + // Set from OffscreenUi::getOpenFile() + 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: root.title !== "" ? hifi.glyphs.scriptUpload : "" + property int iconSize: 40 + + property bool selectDirectory: false; + property bool showHidden: false; + // FIXME implement + property bool multiSelect: false; + property bool saveDialog: false; + property var helper: fileDialogHelper + 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); + + fileDialogItem.keyboardEnabled = HMD.active; + + // 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 : ""; + + // Clear selection when click on external frame. + //frameClicked.connect(function() { d.clearSelection(); }); + + if (selectDirectory) { + currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder)); + d.currentSelectionIsFolder = true; + d.currentSelectionUrl = initialFolder; + } + + helper.contentsChanged.connect(function() { + if (folderListModel) { + // Make folderListModel refresh. + var save = folderListModel.folder; + folderListModel.folder = ""; + folderListModel.folder = save; + } + }); + + fileTableView.forceActiveFocus(); + } + + Item { + id: fileDialogItem + clip: true + width: parent.width + height: parent.height + anchors.margins: 0 + + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + + MouseArea { + // Clear selection when click on internal unused area. + anchors.fill: parent + onClicked: { + d.clearSelection(); + } + } + + Row { + id: navControls + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: parent.left + } + spacing: hifi.dimensions.contentSpacing.x + + GlyphButton { + id: upButton + glyph: hifi.glyphs.levelUp + width: height + size: 30 + enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" + onClicked: d.navigateUp(); + } + + GlyphButton { + id: homeButton + property var destination: helper.home(); + glyph: hifi.glyphs.home + size: 28 + width: height + enabled: d.homeDestination ? true : false + onClicked: d.navigateHome(); + } + } + + TabletComboBox { + id: pathSelector + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: navControls.right + leftMargin: hifi.dimensions.contentSpacing.x + right: parent.right + } + + 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()) { + if (root.selectDirectory) { + currentSelection.text = currentText !== "This PC" ? currentText : ""; + d.currentSelectionUrl = helper.pathToUrl(currentText); + } + fileTableModel.folder = folder; + fileTableView.forceActiveFocus(); + } + } + } + + QtObject { + id: d + property var currentSelectionUrl; + readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl); + property bool currentSelectionIsFolder; + property var backStack: [] + property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } + property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } + property var homeDestination: helper.home(); + + 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) { + if (!root.selectDirectory) { + currentSelection.text = ""; + currentSelectionIsFolder = false; + } + return; + } + + currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); + currentSelectionIsFolder = fileTableView.model.isFolder(row); + if (root.selectDirectory || !currentSelectionIsFolder) { + currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); + } else { + currentSelection.text = ""; + } + } + + function navigateUp() { + if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { + fileTableModel.folder = fileTableModel.parentFolder; + return true; + } + } + + function navigateHome() { + fileTableModel.folder = homeDestination; + return true; + } + + function clearSelection() { + fileTableView.selection.clear(); + fileTableView.currentRow = -1; + update(); + } + } + + 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; + helper.monitorDirectory(""); + update(); + } else { + var needsUpdate = model === driveListModel && folder === folderListModel.folder; + + model = folderListModel; + folderListModel.folder = folder; + helper.monitorDirectory(helper.urlToPath(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++; + } + + d.clearSelection(); + } + } + + Table { + id: fileTableView + 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(); + + sortIndicatorColumn: 0 + sortIndicatorOrder: Qt.AscendingOrder + sortIndicatorVisible: true + + model: fileTableModel + + function updateSort() { + model.sortOrder = sortIndicatorOrder; + model.sortColumn = sortIndicatorColumn; + model.update(); + } + + onSortIndicatorColumnChanged: { updateSort(); } + + onSortIndicatorOrderChanged: { updateSort(); } + + 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(); + } + + function navigateToCurrentRow() { + var row = fileTableView.currentRow + var isFolder = model.isFolder(row); + var file = model.get(row).filePath; + if (isFolder) { + fileTableView.model.folder = helper.pathToUrl(file); + } else { + okAction.trigger(); + } + } + + property string prefix: "" + + function addToPrefix(event) { + if (!event.text || event.text === "") { + return false; + } + var newPrefix = prefix + event.text.toLowerCase(); + var matchedIndex = -1; + for (var i = 0; i < model.count; ++i) { + var name = model.get(i).fileName.toLowerCase(); + if (0 === name.indexOf(newPrefix)) { + matchedIndex = i; + break; + } + } + + if (matchedIndex !== -1) { + fileTableView.selection.clear(); + fileTableView.selection.select(matchedIndex); + fileTableView.currentRow = matchedIndex; + fileTableView.prefix = newPrefix; + } + prefixClearTimer.restart(); + return true; + } + + Timer { + id: prefixClearTimer + interval: 1000 + repeat: false + running: false + onTriggered: fileTableView.prefix = ""; + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Backspace: + case Qt.Key_Tab: + case Qt.Key_Backtab: + event.accepted = false; + break; + + default: + if (addToPrefix(event)) { + event.accepted = true + } else { + event.accepted = false; + } + break; + } + } + } + + TextField { + id: currentSelection + label: selectDirectory ? "Directory:" : "File name:" + anchors { + left: parent.left + right: selectionType.visible ? selectionType.left: parent.right + rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 + bottom: keyboard.top + bottomMargin: hifi.dimensions.contentSpacing.y + } + readOnly: !root.saveDialog + activeFocusOnTab: !readOnly + onActiveFocusChanged: if (activeFocus) { selectAll(); } + onAccepted: okAction.trigger(); + } + + FileTypeSelection { + id: selectionType + anchors { + top: currentSelection.top + left: buttonRow.left + right: parent.right + } + visible: !selectDirectory && filtersCount > 1 + KeyNavigation.left: fileTableView + KeyNavigation.right: openButton + } + + Keyboard { + id: keyboard + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: buttonRow.top + bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0 + } + } + + Row { + id: buttonRow + 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 + KeyNavigation.up: selectionType + KeyNavigation.left: openButton + KeyNavigation.right: fileTableView.contentItem + Keys.onReturnPressed: { canceled(); root.enabled = false } + } + } + + Action { + id: okAction + text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open" + enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false + onTriggered: { + if (!root.selectDirectory && !d.currentSelectionIsFolder + || root.selectDirectory && fileTableView.currentRow === -1) { + okActionTimer.start(); + } else { + fileTableView.navigateToCurrentRow(); + } + } + } + + Timer { + id: okActionTimer + interval: 50 + running: false + repeat: false + onTriggered: { + if (!root.saveDialog) { + selectedFile(d.currentSelectionUrl); + profileRoot.pop(); + return; + } + + // Handle the ambiguity between different cases + // * typed name (with or without extension) + // * full path vs relative vs filename only + var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); + + if (!selection) { + desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) + return; + } + + if (helper.urlIsDir(selection)) { + root.dir = selection; + currentSelection.text = ""; + return; + } + + // Check if the file is a valid target + if (!helper.urlIsWritable(selection)) { + desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Warning, + text: "Unable to write to location " + selection + }) + return; + } + + if (helper.urlExists(selection)) { + var messageBox = desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Do you wish to overwrite " + selection + "?", + }); + var result = messageBox.exec(); + if (OriginalDialogs.StandardButton.Yes !== result) { + return; + } + } + + console.log("Selecting " + selection) + selectedFile(selection); + //root.destroy(); + } + } + + Action { + id: cancelAction + text: "Cancel" + onTriggered: { profileRoot.pop(); } + } + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Backspace: + event.accepted = d.navigateUp(); + break; + + case Qt.Key_Home: + event.accepted = d.navigateHome(); + break; + + } + } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 7a890a452b..7d214237a3 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -123,7 +123,7 @@ Item { function getSectionsHeight() { var totalHeight = 0; for (var i = 0; i < sections.length; i++) { - totalHeight += sections[i].height + totalHeight += sections[i].height + sections[i].getPreferencesHeight(); } console.log(totalHeight); return totalHeight; diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index 36cedbdfd3..f95605120e 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -43,6 +43,14 @@ Preference { } } + function getPreferencesHeight() { + var height = 0; + for (var index = 0; index < d.preferences.length; index++) { + height += d.preferences[index].height; + } + + return height; + } children: [ contentContainer ] height: contentContainer.height + (contentContainer.isCollapsed ? 0 : hifi.dimensions.controlInterlineHeight) diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml index 06880b22cf..13bbafb790 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -12,6 +12,7 @@ import QtQuick 2.5 import "../../../../dialogs" import "../../../../controls-uit" +import "../" Preference { id: root @@ -54,7 +55,7 @@ Preference { Component { id: fileBrowserBuilder; - FileDialog { selectDirectory: true } + TabletFileDialog { selectDirectory: true } } Button { @@ -69,10 +70,13 @@ Preference { selectDirectory: true, dir: fileDialogHelper.pathToUrl(preference.value) }); + browser.selectedFile.connect(function(fileUrl){ console.log(fileUrl); dataTextField.text = fileDialogHelper.urlToPath(fileUrl); }); + + profileRoot.push(browser); } } } From 8fbcead6d18af9d4f13aef16cd41661ea600c682 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 13 Feb 2017 23:44:59 +0000 Subject: [PATCH 5/9] removed/renamed some files --- .../hifi/tablet/tabletWindows/FileDialog.qml | 783 ------------------ .../tabletWindows/TabletScrollingWindow.qml | 47 -- 2 files changed, 830 deletions(-) delete mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml delete mode 100644 interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml deleted file mode 100644 index 0886a25949..0000000000 --- a/interface/resources/qml/hifi/tablet/tabletWindows/FileDialog.qml +++ /dev/null @@ -1,783 +0,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 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs - -import ".." -import "../controls-uit" -import "../styles-uit" -import "../windows" - -import "fileDialog" - -//FIXME implement shortcuts for favorite location -ModalWindow { - id: root - resizable: true - implicitWidth: 480 - implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0) - - minSize: Qt.vector2d(360, 240) - draggable: true - - HifiConstants { id: hifi } - - Settings { - category: "FileDialog" - property alias width: root.width - property alias height: root.height - property alias x: root.x - property alias y: root.y - } - - - // Set from OffscreenUi::getOpenFile() - property alias caption: root.title; - // Set from OffscreenUi::getOpenFile() - 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: root.title !== "" ? hifi.glyphs.scriptUpload : "" - property int iconSize: 40 - - property bool selectDirectory: false; - property bool showHidden: false; - // FIXME implement - property bool multiSelect: false; - property bool saveDialog: false; - property var helper: fileDialogHelper - 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); - - fileDialogItem.keyboardEnabled = HMD.active; - - // 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 : ""; - - // Clear selection when click on external frame. - frameClicked.connect(function() { d.clearSelection(); }); - - if (selectDirectory) { - currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder)); - d.currentSelectionIsFolder = true; - d.currentSelectionUrl = initialFolder; - } - - helper.contentsChanged.connect(function() { - if (folderListModel) { - // Make folderListModel refresh. - var save = folderListModel.folder; - folderListModel.folder = ""; - folderListModel.folder = save; - } - }); - - fileTableView.forceActiveFocus(); - } - - Item { - id: fileDialogItem - clip: true - width: pane.width - height: pane.height - anchors.margins: 0 - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - MouseArea { - // Clear selection when click on internal unused area. - anchors.fill: parent - drag.target: root - onClicked: { - d.clearSelection(); - frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden. - } - } - - Row { - id: navControls - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: parent.left - } - spacing: hifi.dimensions.contentSpacing.x - - GlyphButton { - id: upButton - glyph: hifi.glyphs.levelUp - width: height - size: 30 - enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" - onClicked: d.navigateUp(); - } - - GlyphButton { - id: homeButton - property var destination: helper.home(); - glyph: hifi.glyphs.home - size: 28 - width: height - enabled: d.homeDestination ? true : false - onClicked: d.navigateHome(); - } - } - - ComboBox { - id: pathSelector - anchors { - top: parent.top - topMargin: hifi.dimensions.contentMargin.y - left: navControls.right - leftMargin: hifi.dimensions.contentSpacing.x - right: parent.right - } - - 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()) { - if (root.selectDirectory) { - currentSelection.text = currentText !== "This PC" ? currentText : ""; - d.currentSelectionUrl = helper.pathToUrl(currentText); - } - fileTableModel.folder = folder; - fileTableView.forceActiveFocus(); - } - } - } - - QtObject { - id: d - property var currentSelectionUrl; - readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl); - property bool currentSelectionIsFolder; - property var backStack: [] - property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } - property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } - property var homeDestination: helper.home(); - - 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) { - if (!root.selectDirectory) { - currentSelection.text = ""; - currentSelectionIsFolder = false; - } - return; - } - - currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); - currentSelectionIsFolder = fileTableView.model.isFolder(row); - if (root.selectDirectory || !currentSelectionIsFolder) { - currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); - } else { - currentSelection.text = ""; - } - } - - function navigateUp() { - if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { - fileTableModel.folder = fileTableModel.parentFolder; - return true; - } - } - - function navigateHome() { - fileTableModel.folder = homeDestination; - return true; - } - - function clearSelection() { - fileTableView.selection.clear(); - fileTableView.currentRow = -1; - update(); - } - } - - 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; - helper.monitorDirectory(""); - update(); - } else { - var needsUpdate = model === driveListModel && folder === folderListModel.folder; - - model = folderListModel; - folderListModel.folder = folder; - helper.monitorDirectory(helper.urlToPath(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++; - } - - d.clearSelection(); - } - } - - Table { - id: fileTableView - 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(); - - sortIndicatorColumn: 0 - sortIndicatorOrder: Qt.AscendingOrder - sortIndicatorVisible: true - - model: fileTableModel - - function updateSort() { - model.sortOrder = sortIndicatorOrder; - model.sortColumn = sortIndicatorColumn; - model.update(); - } - - onSortIndicatorColumnChanged: { updateSort(); } - - onSortIndicatorOrderChanged: { updateSort(); } - - 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(); - } - - function navigateToCurrentRow() { - var row = fileTableView.currentRow - var isFolder = model.isFolder(row); - var file = model.get(row).filePath; - if (isFolder) { - fileTableView.model.folder = helper.pathToUrl(file); - } else { - okAction.trigger(); - } - } - - property string prefix: "" - - function addToPrefix(event) { - if (!event.text || event.text === "") { - return false; - } - var newPrefix = prefix + event.text.toLowerCase(); - var matchedIndex = -1; - for (var i = 0; i < model.count; ++i) { - var name = model.get(i).fileName.toLowerCase(); - if (0 === name.indexOf(newPrefix)) { - matchedIndex = i; - break; - } - } - - if (matchedIndex !== -1) { - fileTableView.selection.clear(); - fileTableView.selection.select(matchedIndex); - fileTableView.currentRow = matchedIndex; - fileTableView.prefix = newPrefix; - } - prefixClearTimer.restart(); - return true; - } - - Timer { - id: prefixClearTimer - interval: 1000 - repeat: false - running: false - onTriggered: fileTableView.prefix = ""; - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - case Qt.Key_Tab: - case Qt.Key_Backtab: - event.accepted = false; - break; - - default: - if (addToPrefix(event)) { - event.accepted = true - } else { - event.accepted = false; - } - break; - } - } - } - - TextField { - id: currentSelection - label: selectDirectory ? "Directory:" : "File name:" - anchors { - left: parent.left - right: selectionType.visible ? selectionType.left: parent.right - rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 - bottom: keyboard.top - bottomMargin: hifi.dimensions.contentSpacing.y - } - readOnly: !root.saveDialog - activeFocusOnTab: !readOnly - onActiveFocusChanged: if (activeFocus) { selectAll(); } - onAccepted: okAction.trigger(); - } - - FileTypeSelection { - id: selectionType - anchors { - top: currentSelection.top - left: buttonRow.left - right: parent.right - } - visible: !selectDirectory && filtersCount > 1 - KeyNavigation.left: fileTableView - KeyNavigation.right: openButton - } - - Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttonRow.top - bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0 - } - } - - Row { - id: buttonRow - 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 - KeyNavigation.up: selectionType - KeyNavigation.left: openButton - KeyNavigation.right: fileTableView.contentItem - Keys.onReturnPressed: { canceled(); root.enabled = false } - } - } - - Action { - id: okAction - text: currentSelection.text ? (root.selectDirectory && fileTableView.currentRow === -1 ? "Choose" : (root.saveDialog ? "Save" : "Open")) : "Open" - enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false - onTriggered: { - if (!root.selectDirectory && !d.currentSelectionIsFolder - || root.selectDirectory && fileTableView.currentRow === -1) { - okActionTimer.start(); - } else { - fileTableView.navigateToCurrentRow(); - } - } - } - - Timer { - id: okActionTimer - interval: 50 - running: false - repeat: false - onTriggered: { - if (!root.saveDialog) { - selectedFile(d.currentSelectionUrl); - root.destroy() - return; - } - - // Handle the ambiguity between different cases - // * typed name (with or without extension) - // * full path vs relative vs filename only - var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); - - if (!selection) { - desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) - return; - } - - if (helper.urlIsDir(selection)) { - root.dir = selection; - currentSelection.text = ""; - return; - } - - // Check if the file is a valid target - if (!helper.urlIsWritable(selection)) { - desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Warning, - text: "Unable to write to location " + selection - }) - return; - } - - if (helper.urlExists(selection)) { - var messageBox = desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Question, - buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, - text: "Do you wish to overwrite " + selection + "?", - }); - var result = messageBox.exec(); - if (OriginalDialogs.StandardButton.Yes !== result) { - return; - } - } - - console.log("Selecting " + selection) - selectedFile(selection); - root.destroy(); - } - } - - Action { - id: cancelAction - text: "Cancel" - onTriggered: { canceled(); root.shown = false; } - } - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Backspace: - event.accepted = d.navigateUp(); - break; - - case Qt.Key_Home: - event.accepted = d.navigateHome(); - break; - - } - } -} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml deleted file mode 100644 index 5b43d20109..0000000000 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletScrollingWindow.qml +++ /dev/null @@ -1,47 +0,0 @@ -// -// TabletsScrollingWindow.qml -// -// Created by Dante Ruiz on 9 Feb 2017 -// 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 -import QtGraphicalEffects 1.0 - -import "." -import "../../../styles-uit" -import "../../../controls-uit" as HiFiControls -Item { - - id: window - HifiConstants { id: hifi } - - children: [ pane ] - property var footer: Item {} - property var pane: Item { - anchors.fill: parent - - Rectangle { - id: contentBackground - anchors.fill: parent - width: 480 - //gradient: Gradient { - //GradientStop { - //position: 0 - //color: "#2b2b2b" - - //} - - //GradientStop { - //position: 1 - //color: "#0f212e" - //} - //} - } - } -} From 773ade5b612c18797cb9f816760f8b5c1f43532f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 14 Feb 2017 15:42:59 +0000 Subject: [PATCH 6/9] removing qml errors --- .../qml/controls-uit/ContentSection.qml | 8 +- .../qml/controls-uit/TabletContentSection.qml | 138 ++++++++++++++++++ .../tabletWindows/preferences/Section.qml | 2 +- 3 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 interface/resources/qml/controls-uit/TabletContentSection.qml diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml index 012b803738..4f8197d6c7 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -58,14 +58,14 @@ Column { Rectangle { id: shadow - width: 480//frame ? frame.width: 480 + width: frame ? frame.width: 480 height: 1 color: hifi.colors.baseGrayShadow x: -hifi.dimensions.contentMargin.x } Rectangle { - width: 480 //frame ? frame.width : 480 + width: frame ? frame.width : 480 height: 1 color: hifi.colors.baseGrayHighlight x: -hifi.dimensions.contentMargin.x @@ -121,8 +121,8 @@ Column { LinearGradient { id: bottomBar - visible: false //(desktop ? desktop.gradientsSupported : false) && isCollapsible - width: 480 //(frame ? frame.width : 480) + visible: (desktop ? desktop.gradientsSupported : false) && isCollapsible + width: (frame ? frame.width : 480) height: visible ? 4 : 0 x: -hifi.dimensions.contentMargin.x anchors.top: heading.bottom diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml new file mode 100644 index 0000000000..722d597c54 --- /dev/null +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -0,0 +1,138 @@ +// +// ContentSection.qml +// +// Created by David Rowe on 16 Feb 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 QtGraphicalEffects 1.0 + +import "../styles-uit" + +Column { + property string name: "Content Section" + property bool isFirst: false + property bool isCollapsible: false // Set at creation. + property bool isCollapsed: false + + spacing: 0 // Defer spacing decisions to individual controls. + + anchors { + left: parent.left + leftMargin: hifi.dimensions.contentMargin.x + right: parent.right + rightMargin: hifi.dimensions.contentMargin.x + } + + function toggleCollapsed() { + if (isCollapsible) { + isCollapsed = !isCollapsed; + for (var i = 1; i < children.length; i++) { + children[i].visible = !isCollapsed; + } + } + } + + Item { + id: sectionName + anchors.left: parent.left + anchors.right: parent.right + height: leadingSpace.height + topBar.height + heading.height + bottomBar.height + + Item { + id: leadingSpace + width: 1 + height: isFirst ? 7 : 0 + anchors.top: parent.top + } + + Item { + id: topBar + visible: !isFirst + height: visible ? 2 : 0 + anchors.top: leadingSpace.bottom + + Rectangle { + id: shadow + width: frame ? frame.width: 480 + height: 1 + color: hifi.colors.baseGrayShadow + x: -hifi.dimensions.contentMargin.x + } + + Rectangle { + width: 480//480frame ? frame.width : 480 + height: 1 + color: hifi.colors.baseGrayHighlight + x: -hifi.dimensions.contentMargin.x + anchors.top: shadow.bottom + } + } + + Item { + id: heading + anchors { + left: parent.left + right: parent.right + top: topBar.bottom + } + height: isCollapsible ? 36 : 28 + + RalewayRegular { + id: title + anchors { + left: parent.left + top: parent.top + topMargin: 12 + } + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + text: name + color: hifi.colors.lightGrayText + } + + HiFiGlyphs { + anchors { + top: title.top + topMargin: -9 + right: parent.right + rightMargin: -4 + } + size: hifi.fontSizes.disclosureButton + text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse + color: hifi.colors.lightGrayText + visible: isCollapsible + } + + MouseArea { + // Events are propogated so that any active control is defocused. + anchors.fill: parent + propagateComposedEvents: true + onPressed: { + toggleCollapsed(); + mouse.accepted = false; + } + } + } + + LinearGradient { + id: bottomBar + visible: false + width: 480 + height: visible ? 4 : 0 + x: -hifi.dimensions.contentMargin.x + anchors.top: heading.bottom + start: Qt.point(0, 0) + end: Qt.point(0, 4) + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.darkGray } + GradientStop { position: 1.0; color: hifi.colors.baseGray } // Equivalent of darkGray0 over baseGray background. + } + cached: true + } + } +} diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index f95605120e..f1cef1878a 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -57,7 +57,7 @@ Preference { Component.onCompleted: d.buildPreferences(); - HiFiControls.ContentSection { + HiFiControls.TabletContentSection { id: contentContainer name: root.name isFirst: root.isFirst From 9d5f392be33f45321eef947d939cf8016e9ebc94 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 14 Feb 2017 15:45:01 +0000 Subject: [PATCH 7/9] removed more qml log errors --- interface/resources/qml/controls-uit/TabletContentSection.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml index 722d597c54..b065ad2159 100644 --- a/interface/resources/qml/controls-uit/TabletContentSection.qml +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -58,14 +58,14 @@ Column { Rectangle { id: shadow - width: frame ? frame.width: 480 + width: 480 height: 1 color: hifi.colors.baseGrayShadow x: -hifi.dimensions.contentMargin.x } Rectangle { - width: 480//480frame ? frame.width : 480 + width: 480 height: 1 color: hifi.colors.baseGrayHighlight x: -hifi.dimensions.contentMargin.x From aa164bbf8fbde3b0770fdea16473598fc6a5af88 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 14 Feb 2017 15:55:32 +0000 Subject: [PATCH 8/9] diff minimize --- interface/resources/qml/controls-uit/ContentSection.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml index 4f8197d6c7..47a13e9262 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -58,14 +58,14 @@ Column { Rectangle { id: shadow - width: frame ? frame.width: 480 + width: frame.width height: 1 color: hifi.colors.baseGrayShadow x: -hifi.dimensions.contentMargin.x } Rectangle { - width: frame ? frame.width : 480 + width: frame.width height: 1 color: hifi.colors.baseGrayHighlight x: -hifi.dimensions.contentMargin.x @@ -121,8 +121,8 @@ Column { LinearGradient { id: bottomBar - visible: (desktop ? desktop.gradientsSupported : false) && isCollapsible - width: (frame ? frame.width : 480) + visible: desktop.gradientsSupported && isCollapsible + width: frame.width height: visible ? 4 : 0 x: -hifi.dimensions.contentMargin.x anchors.top: heading.bottom From 968ba1e03dbe28e4ab864a1c9bacd3498ad42581 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 14 Feb 2017 16:08:25 +0000 Subject: [PATCH 9/9] updated file headers --- interface/resources/qml/controls-uit/TabletComboBox.qml | 2 +- interface/resources/qml/controls-uit/TabletContentSection.qml | 2 +- .../qml/hifi/tablet/tabletWindows/TabletFileDialog.qml | 2 +- .../qml/hifi/tablet/tabletWindows/preferences/Preference.qml | 2 +- .../qml/hifi/tablet/tabletWindows/preferences/Section.qml | 2 +- .../tabletWindows/preferences/TabletBrowsablePreference.qml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/controls-uit/TabletComboBox.qml b/interface/resources/qml/controls-uit/TabletComboBox.qml index cd26494ef1..e5dec315e5 100644 --- a/interface/resources/qml/controls-uit/TabletComboBox.qml +++ b/interface/resources/qml/controls-uit/TabletComboBox.qml @@ -1,7 +1,7 @@ // // ComboBox.qml // -// Created by Bradley Austin David on 27 Jan 2016 +// Created by Dante Ruiz on 13 Feb 2017 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml index b065ad2159..c34f4afdd6 100644 --- a/interface/resources/qml/controls-uit/TabletContentSection.qml +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -1,7 +1,7 @@ // // ContentSection.qml // -// Created by David Rowe on 16 Feb 2016 +// Created by Dante Ruiz on 13 Feb 2017 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index 205a93a935..a3e94152b8 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -1,7 +1,7 @@ // // FileDialog.qml // -// Created by Bradley Austin Davis on 14 Jan 2016 +// Created by Bradley Dante Ruiz on 13 Feb 2017 // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml index 1d72197382..9986c85445 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Preference.qml @@ -1,7 +1,7 @@ // // Preference.qml // -// Created by Bradley Austin Davis on 18 Jan 2016 +// Created by Bradley Dante Ruiz on 13 Feb 2017 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index f1cef1878a..3d6dfa10ce 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -1,7 +1,7 @@ // // Section.qml // -// Created by Bradley Austin Davis on 18 Jan 2016 +// Created by Bradley Dante Ruiz on 13 Feb 2017 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml index 13bbafb790..8c0e934971 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -1,7 +1,7 @@ // // BrowsablePreference.qml // -// Created by Bradley Austin Davis on 18 Jan 2016 +// Created by Dante Ruiz Davis on 13 Feb 2017 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0.