diff --git a/src/qml/ImagesView.qml b/src/qml/ImagesView.qml index 6612f15..4b161f4 100644 --- a/src/qml/ImagesView.qml +++ b/src/qml/ImagesView.qml @@ -1,359 +1,374 @@ import QtQml 2.2 import QtQuick 2.0 import QtQuick.Controls 1.2 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.2 import org.kde.kamoso 3.0 import org.kde.purpose 1.0 import org.kde.kirigami 2.4 as Kirigami StackView { id: stack property string mimeFilter property alias nameFilter: view.nameFilter clip: true function pathOrUrl(url) { var urlstr = url.toString(); if (urlstr.indexOf("file://") == 0) { return urlstr.substring(7); } return url; } Component { id: headerComponent ColumnLayout { spacing: 0 Kirigami.Heading { Layout.fillWidth: true Layout.leftMargin: Kirigami.Units.smallSpacing Layout.bottomMargin: Kirigami.Units.largeSpacing level: 1 text: i18n("Share") elide: Text.ElideRight } Repeater { model: view.selection delegate: Kirigami.AbstractListItem { id: delegate Layout.minimumHeight: Kirigami.Units.gridUnit * 3 spacing: 0 RowLayout { ImageThumbnail { Layout.fillHeight: true width: height path: modelData } QQC2.Label { Layout.fillWidth: true text: modelData.substring(modelData.lastIndexOf('/')+1); elide: Text.ElideLeft } } } } } } Component { id: chooseShareComponent ColumnLayout { property var selection spacing: 0 Loader { Layout.fillWidth: true Layout.maximumHeight: item.Layout.maximumHeight sourceComponent: headerComponent } AlternativesView { id: altsView Layout.fillWidth: true Layout.fillHeight: true pluginType: "Export" inputData: { "urls": view.selection, "mimeType": stack.mimeFilter } verticalLayoutDirection: ListView.BottomToTop delegate: Kirigami.BasicListItem { label: display icon: model.iconName onClicked: altsView.createJob(index); } onFinished: stack.replace({ item: sharedComponent, properties: { text: output.url }, replace: true }) } Kirigami.BasicListItem { label: i18n("Back") icon: "go-previous" onClicked: stack.pop() } } } Component { id: sharedComponent ColumnLayout { property alias text: field.text spacing: 0 Loader { Layout.fillWidth: true Layout.maximumHeight: item.Layout.maximumHeight sourceComponent: headerComponent } Item { Layout.fillHeight: true Layout.fillWidth: true } QQC2.TextField { id: field Layout.fillWidth: true readOnly: true focus: true onTextChanged: { selectAll(); copy(); } } Item { Layout.fillHeight: true Layout.fillWidth: true } QQC2.Label { Layout.fillHeight: true Layout.alignment: Qt.AlignCenter text: i18n("Media now exported") } Kirigami.Separator { Layout.fillWidth: true } Kirigami.BasicListItem { label: i18n("Back") onClicked: { stack.pop() } } } } Component { id: configureComponent ColumnLayout { property var selection spacing: 0 Kirigami.Heading { Layout.fillWidth: true Layout.leftMargin: Kirigami.Units.smallSpacing Layout.bottomMargin: Kirigami.Units.largeSpacing level: 1 text: i18n("Configure Kamoso") elide: Text.ElideRight } Kirigami.FormLayout { Item { Kirigami.FormData.isSection: true Kirigami.FormData.label: i18n("Save Locations") } // Pictures location RowLayout { Layout.fillWidth: true Kirigami.FormData.label: i18n("Pictures:") QQC2.TextField { readOnly: true Layout.fillWidth: true text: stack.pathOrUrl(config.saveUrl) } QQC2.Button { hoverEnabled: true icon.name: "document-open" QQC2.ToolTip { visible: parent.hovered text: i18n("Choose the folder where Kamoso will save pictures") // TODO: Remove this once Kamoso can depend on // Frameworks 5.55, where this is fixed upstream z: 999 } onClicked: { dirSelector.visible = true } FileDialog { id: dirSelector title: i18n("Choose the folder where Kamoso will save pictures") folder: config.saveUrl selectMultiple: false selectExisting: true selectFolder: true onFileUrlChanged: { config.saveUrl = dirSelector.fileUrl config.save() } } } } // Videos location RowLayout { Layout.fillWidth: true Kirigami.FormData.label: i18n("Videos:") QQC2.TextField { readOnly: true Layout.fillWidth: true text: stack.pathOrUrl(config.saveVideos) } QQC2.Button { hoverEnabled: true icon.name: "document-open" QQC2.ToolTip { visible: parent.hovered text: i18n("Choose the folder where Kamoso will save videos") // TODO: Remove this once Kamoso can depend on // Frameworks 5.55, where this is fixed upstream z: 999 } onClicked: { videoDirSelector.visible = true } } FileDialog { id: videoDirSelector title: i18n("Choose the folder where Kamoso will save videos") folder: config.saveVideos selectMultiple: false selectExisting: true selectFolder: true onFileUrlChanged: { config.saveVideos = videoDirSelector.fileUrl config.save() } } } + + Item { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: i18n("Cameras") + } + + ComboBox { + Layout.fillWidth: parent + model: devicesModel + textRole: "display" + enabled: count>1 + onAccepted: { + devicesModel.playingDeviceUdi = udi + } + } } // Otherwise the back button might not always be right on the bottom Item { Layout.fillHeight: true } Kirigami.BasicListItem { label: i18n("Back") icon: "go-previous" onClicked: stack.pop() } } } initialItem: Item { ColumnLayout { anchors.fill: parent spacing: 0 DirectoryView { id: view QQC2.ScrollBar.vertical: QQC2.ScrollBar {} clip: true Layout.fillWidth: true Layout.fillHeight: true mimeFilter: [stack.mimeFilter] } Kirigami.Heading { Layout.fillWidth: true Layout.fillHeight: true verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter visible: view.count == 0 level: 2 text: xi18nc("@info", "There are no images in %1", stack.pathOrUrl(config.saveUrl)) opacity: 0.6 } Kirigami.Separator { Layout.fillWidth: true } Kirigami.BasicListItem { enabled: view.selection.length > 0 icon: "document-share" label: enabled? i18np("Share %1 Item...", "Share %1 Items...", view.selection.length) : i18n("Share Item...") onClicked: stack.push({ item: chooseShareComponent, properties: { selection: view.selection } }) } Kirigami.BasicListItem { enabled: view.selection.length > 0 icon: "user-trash" label: enabled ? i18np("Move %1 Item to Trash", "Move %1 Items to Trash", view.selection.length) : i18n("Move Item to Trash") onClicked: { console.log("Trash, FFS!!", view.selection); webcam.trashFiles(view.selection); } } Kirigami.BasicListItem { icon: "folder-open" label: i18n("Open Pictures Folder") onClicked: Qt.openUrlExternally(config.saveUrl) } Kirigami.BasicListItem { icon: "configure" label: i18n("Configure Kamoso...") onClicked: stack.push({ item: configureComponent, properties: { selection: view.selection } }) } } } } diff --git a/src/qml/Main.qml b/src/qml/Main.qml index 019187d..0136034 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -1,237 +1,219 @@ import QtQuick 2.5 import QtQuick.Controls 1.1 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import KamosoQtGStreamer 1.0 import org.kde.kirigami 2.0 as Kirigami import org.kde.kamoso 3.0 Kirigami.ApplicationWindow { id: root visible: true title: i18n("Kamoso") pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.None Component.onCompleted: { width = 700 height = width*3/4 } function awesomeAnimation(path) { // tada.x = visor.x // tada.y = 0 // tada.width = visor.width // tada.height = visor.height tada.source = "file://"+path tada.state = "go" tada.state = "done" // tada.visible = true } Image { id: tada z: 10 width: 10 height: 10 fillMode: Image.PreserveAspectFit states: [ State { name: "go" PropertyChanges { target: tada; x: visor.x } PropertyChanges { target: tada; y: visor.y } PropertyChanges { target: tada; width: visor.width } PropertyChanges { target: tada; height: visor.height } PropertyChanges { target: tada; opacity: 1 } }, State { name: "done" PropertyChanges { target: tada; x: 0 } PropertyChanges { target: tada; y: root.height } PropertyChanges { target: tada; width: Kirigami.Units.gridUnit } PropertyChanges { target: tada; height: Kirigami.Units.gridUnit } PropertyChanges { target: tada; opacity: 0.5 } } ] transitions: [ Transition { from: "go"; to: "done" NumberAnimation { target: tada properties: "width,height"; duration: 700; easing.type: Easing.InCubic } NumberAnimation { target: tada properties: "x,y"; duration: 700; easing.type: Easing.InCubic } NumberAnimation { target: tada properties: "opacity"; duration: 300 } } ] } Mode { id: photoMode mimes: "image/jpeg" checkable: false iconName: "camera-photo-symbolic" text: i18n("Take a Picture") nameFilter: "picture_*" onTriggered: { whites.showAll() webcam.takePhoto() } Connections { target: webcam onPhotoTaken: awesomeAnimation(path) } } Mode { id: burstMode mimes: "image/jpeg" checkable: true iconName: "burst" text: i18n("Capture a Burst") property int photosTaken: 0 modeInfo: (photosTaken>0 ? i18np("1 photo", "%1 photos", photosTaken) : "") + (checked? "..." : "") nameFilter: "picture_*" onCheckedChanged: if (checked) { photosTaken = 0 } readonly property var smth: Timer { id: burstTimer running: burstMode.checked interval: 1000 repeat: true onTriggered: { whites.showAll() webcam.takePhoto() burstMode.photosTaken++; } } } Mode { id: videoMode mimes: "video/x-matroska" checkable: true iconName: "record" text: i18n("Record a Video") modeInfo: webcam.recordingTime nameFilter: "video_*" onCheckedChanged: { webcam.isRecording = checked; } } contextDrawer: Kirigami.OverlayDrawer { edge: Qt.RightEdge drawerOpen: false handleVisible: true modal: true leftPadding: 0 topPadding: 0 rightPadding: 0 bottomPadding: 0 contentItem: ImagesView { id: view implicitWidth: Kirigami.Units.gridUnit * 20 mimeFilter: root.pageStack.currentItem.actions.main.mimes nameFilter: root.pageStack.currentItem.actions.main.nameFilter } } globalDrawer: Kirigami.OverlayDrawer { edge: Qt.LeftEdge drawerOpen: false handleVisible: true modal: true width: Kirigami.Units.gridUnit * 20 leftPadding: Kirigami.Units.smallSpacing topPadding: Kirigami.Units.smallSpacing rightPadding: Kirigami.Units.smallSpacing bottomPadding: Kirigami.Units.smallSpacing contentItem: Config { id: configView QQC2.ScrollBar.vertical: QQC2.ScrollBar {} header: QQC2.Control { height: effectsGalleryHeading.height + Kirigami.Units.largeSpacing Kirigami.Heading { id: effectsGalleryHeading level: 1 color: Kirigami.Theme.textColor elide: Text.ElideRight text: i18n("Effects Gallery") } } } } Shortcut { sequence: "Return" onActivated: visor.actions.main.triggered(null) } pageStack.initialPage: Kirigami.Page { id: visor bottomPadding: 0 topPadding: 0 rightPadding: 0 leftPadding: 0 actions { left: videoMode main: photoMode right: burstMode } Rectangle { anchors.fill: parent color: "black" z: -1 } VideoItem { surface: videoSurface1 anchors.fill: parent - - ColumnLayout { - spacing: Kirigami.Units.smallSpacing - anchors.margins: Kirigami.Units.smallSpacing - anchors.top: parent.top - anchors.left: parent.left - visible: devicesModel.count>1 - - Repeater { - model: devicesModel - delegate: Button { - width: 30 - iconName: "camera-web" - tooltip: display - onClicked: devicesModel.playingDeviceUdi = udi - } - } - } } Text { anchors { horizontalCenter: parent.horizontalCenter top: parent.top margins: 20 } text: videoMode.checked ? videoMode.modeInfo : burstMode.checked ? burstMode.modeInfo : "" color: "white" styleColor: "black" font.pointSize: 20 style: Text.Outline } } }