Commit d3c4c4d6 authored by Noah Davis's avatar Noah Davis 🌵
Browse files

Add InfoSidebar, InfoDrawer and TagInput

InfoSidebar is a QQC2 Page that is meant to be separated from the
ImageViewPage's ListView by a moveable separator.

InfoDrawer is basically the same as the OverlayDrawer that was there
before.

InfoSidebar and InfoDrawer share the same content via
InfoDrawerSidebarBase.

TagInput is a QQC2 ComboBox that allows tags to be added to images.

ImageViewPage: Use InfoSidebar and InfoDrawer
parent 5741308e
This diff is collapsed.
/* SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQml 2.15
import QtQuick.Window 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.koko 0.1 as Koko
import org.kde.kcoreaddons 1.0 as KCA
import org.kde.koko.private 0.1 as KokoPrivate
Kirigami.OverlayDrawer {
id: root
property Koko.Exiv2Extractor extractor
drawerOpen: false
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
handleVisible: false
leftPadding: root.mirrored && vScrollBar.visible ? vScrollBar.width : 0
rightPadding: !root.mirrored && vScrollBar.visible ? vScrollBar.width : 0
topPadding: Math.ceil(header.implicitHeight) + header.y
bottomPadding: 0
Kirigami.Heading {
id: header
parent: content.parent
z: 1
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: Kirigami.Units.largeSpacing
anchors.topMargin: Kirigami.Units.largeSpacing
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
level: 2
text: i18n("Metadata")
}
// QQC2 ScrollView makes it surprisingly difficult to control the
// content size and implicit size without binding loops or glitches.
// ScrollView completely ignores the Flickable's implicit size.
// Using plain Flickable with ScrollBar instead.
contentItem: InfoDrawerSidebarBase {
id: content
extractor: root.extractor
topMargin: 0
QQC2.ScrollBar.vertical: QQC2.ScrollBar {
id: vScrollBar
parent: content.parent
anchors.left: parent.contentItem.right
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}
Component.onCompleted: root.open()
}
/* SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQml 2.15
import QtQuick.Window 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.koko 0.1 as Koko
import org.kde.kcoreaddons 1.0 as KCA
import org.kde.koko.private 0.1 as KokoPrivate
Flickable {
id: flickable
required property Koko.Exiv2Extractor extractor
implicitWidth: column.implicitWidth + leftMargin + rightMargin
implicitHeight: contentHeight + topMargin + bottomMargin
contentWidth: width - leftMargin - rightMargin
contentHeight: column.implicitHeight
leftMargin: Kirigami.Units.largeSpacing
rightMargin: Kirigami.Units.largeSpacing
topMargin: Kirigami.Units.largeSpacing
bottomMargin: Kirigami.Units.largeSpacing
clip: true
boundsBehavior: Flickable.StopAtBounds
pixelAligned: true
Kirigami.WheelHandler { target: flickable }
ColumnLayout {
id: column
property real availableWidth: width - leftPadding - rightPadding
spacing: Kirigami.Units.smallSpacing
width: parent.width
focus: true
Kirigami.Heading {
Layout.fillWidth: true
level: 4
topPadding: Kirigami.Units.smallSpacing
text: i18n("File Name")
}
QQC2.Label {
Layout.fillWidth: true
text: flickable.extractor.simplifiedPath
wrapMode: Text.Wrap
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Dimension")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.width > 0 && flickable.extractor.height > 0
}
QQC2.Label {
Layout.fillWidth: true
text: i18nc("dimensions", "%1 x %2", flickable.extractor.width, flickable.extractor.height)
visible: flickable.extractor.width > 0 && flickable.extractor.height > 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Size")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.size !== 0
}
QQC2.Label {
Layout.fillWidth: true
text: KCA.Format.formatByteSize(flickable.extractor.size, 2)
visible: flickable.extractor.size !== 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Created")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.time.length > 0
}
QQC2.Label {
Layout.fillWidth: true
text: flickable.extractor.time
visible: flickable.extractor.time.length > 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Model")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.model.length > 0
}
QQC2.Label {
Layout.fillWidth: true
text: flickable.extractor.model
visible: flickable.extractor.model.length > 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Latitude")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.gpsLatitude !== 0
}
QQC2.Label {
Layout.fillWidth: true
text: flickable.extractor.gpsLatitude
visible: flickable.extractor.gpsLatitude !== 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Longitude")
topPadding: Kirigami.Units.smallSpacing
visible: flickable.extractor.gpsLongitude !== 0
}
QQC2.Label {
Layout.fillWidth: true
text: flickable.extractor.gpsLongitude
visible: flickable.extractor.gpsLongitude !== 0
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Rating")
topPadding: Kirigami.Units.smallSpacing
}
Row {
// stars look disconnected with higher spacing
spacing: Kirigami.Settings.isMobile ? Kirigami.Units.smallSpacing : Math.round(Kirigami.Units.smallSpacing / 4)
Accessible.role: Accessible.List
Accessible.name: i18n("Current rating %1", flickable.extractor.rating)
Repeater {
model: [ 1, 3, 5, 7, 9 ]
QQC2.AbstractButton {
activeFocusOnTab: true
width: height
height: Kirigami.Units.iconSizes.smallMedium
text: i18n("Set rating to %1", ratingTo)
property int ratingTo: {
if (flickable.extractor.rating == modelData + 1) {
return modelData
} else if (flickable.extractor.rating == modelData) {
return modelData - 1
} else {
return modelData + 1
}
}
contentItem: Kirigami.Icon {
source: flickable.extractor.rating > modelData ? "rating" :
flickable.extractor.rating < modelData ? "rating-unrated" : "rating-half"
width: parent.width
height: parent.height
color: (parent.focusReason == Qt.TabFocusReason || parent.focusReason == Qt.BacktabFocusReason) && parent.activeFocus ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
}
onClicked: {
flickable.extractor.rating = ratingTo
}
}
}
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Description")
topPadding: Kirigami.Units.smallSpacing
}
QQC2.TextArea {
id: imageDescription
text: flickable.extractor.description
Layout.fillWidth: true
verticalAlignment: Qt.AlignTop
placeholderText: i18n("Image description…")
KeyNavigation.priority: KeyNavigation.BeforeItem
Keys.onTabPressed: nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
onEditingFinished: {
flickable.extractor.description = text
}
}
Kirigami.Heading {
Layout.fillWidth: true
level: 4
text: i18n("Tags")
topPadding: Kirigami.Units.smallSpacing
}
RowLayout {
id: tagInputLayout
spacing: Kirigami.Units.smallSpacing
Layout.fillWidth: true
TagInput {
id: tagInput
Layout.fillWidth: true
extractor: flickable.extractor
}
QQC2.Button {
enabled: tagInput.editText
display: QQC2.AbstractButton.IconOnly
icon.name: "list-add"
text: i18n("Add Tag")
onClicked: tagInput.accepted()
}
}
Flow {
Layout.preferredWidth: tagInputLayout.implicitWidth
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
Repeater {
model: flickable.extractor.tags
Tag {
text: modelData
icon.name: "edit-delete-remove"
actionText: i18n("Remove %1 tag", modelData)
onClicked: {
const index = flickable.extractor.tags.indexOf(modelData)
if (index > -1) {
flickable.extractor.tags.splice(index, 1)
}
}
}
}
}
}
}
/* SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQml 2.15
import QtQuick.Window 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.koko 0.1 as Koko
import org.kde.kcoreaddons 1.0 as KCA
QQC2.Page {
id: root
required property Koko.Exiv2Extractor extractor
signal closed()
leftPadding: root.mirrored && vScrollBar.visible ? vScrollBar.width : 0
rightPadding: !root.mirrored && vScrollBar.visible ? vScrollBar.width : 0
topPadding: 0
bottomPadding: 0
header: QQC2.ToolBar {
implicitHeight: closeButton.implicitHeight
leftPadding: 0; rightPadding: 0; topPadding: 0; bottomPadding: 0
contentItem: RowLayout {
spacing: Kirigami.Units.smallSpacing
Kirigami.Heading {
leftPadding: Kirigami.Units.largeSpacing
rightPadding: leftPadding
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true
Layout.fillHeight: true
level: 2
text: i18n("Metadata")
}
QQC2.ToolButton {
id: closeButton
icon.name: "window-close"
icon.width: Kirigami.Units.iconSizes.sizeForLabels
icon.height: Kirigami.Units.iconSizes.sizeForLabels
onClicked: root.closed()
}
}
}
// QQC2 ScrollView makes it surprisingly difficult to control the
// content size and implicit size without binding loops or glitches.
// ScrollView completely ignores the Flickable's implicit size.
// Using plain Flickable with ScrollBar instead.
contentItem: InfoDrawerSidebarBase {
id: content
extractor: root.extractor
QQC2.ScrollBar.vertical: QQC2.ScrollBar {
id: vScrollBar
parent: content.parent
anchors.left: parent.contentItem.right
anchors.top: parent.header.bottom
anchors.bottom: parent.bottom
}
}
Component.onCompleted: {
root.contentItem.opacity = 1
}
onClosed: {
root.contentItem.opacity = 0
}
}
/* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.koko 0.1 as Koko
QQC2.ComboBox {
id: comboBox
required property Koko.Exiv2Extractor extractor
editable: true
model: tagsListModel
Koko.ImageTagsModel {
id: imageTagsModel
onTagsChanged: if (tagsListModel.count > 0) {
tagsListModel.clear()
imageTagsModel.tags.forEach((element) => {
if (!comboBox.extractor.tags.includes(element)) {
tagsListModel.append({ tag: element })
}
})
}
}
// For some reason, using an array as a model directly causes the
// contentItem to show the first item when created instead of being blank.
ListModel {
id: tagsListModel
Component.onCompleted: imageTagsModel.tags.forEach((element) => {
if (!comboBox.extractor.tags.includes(element)) {
tagsListModel.append({ tag: element })
}
})
}
Connections {
target: comboBox.extractor
function onTagsChanged() {
tagsListModel.clear()
imageTagsModel.tags.forEach((element) => {
if (!comboBox.extractor.tags.includes(element)) {
tagsListModel.append({ tag: element })
}
})
}
}
QQC2.Label {
id: placeholder
x: comboBox.contentItem.x + comboBox.contentItem.leftPadding
y: comboBox.contentItem.y + comboBox.contentItem.topPadding
width: comboBox.contentItem.width - comboBox.contentItem.leftPadding - comboBox.contentItem.rightPadding
height: comboBox.contentItem.height - comboBox.contentItem.topPadding - comboBox.contentItem.bottomPadding
text: i18n("Add new tag…")
font: comboBox.font
opacity: 0.5
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
visible: !comboBox.editText
elide: Text.ElideRight
}
onAccepted: {
const text = comboBox.editText.trim()
if (text.length > 0) {
comboBox.extractor.tags.push(text)
}
comboBox.editText = ""
}
}
......@@ -9,6 +9,9 @@ SPDX-License-Identifier: LGPL-2.0-or-later
<file>qml/EditorView.qml</file>
<file>qml/ImageDelegate.qml</file>
<file>qml/ImageViewPage.qml</file>
<file>qml/InfoDrawer.qml</file>
<file>qml/InfoDrawerSidebarBase.qml</file>
<file>qml/InfoSidebar.qml</file>
<file>qml/Main.qml</file>
<file>qml/OverviewControl.qml</file>
<file>qml/SelectionButton.qml</file>
......@@ -18,6 +21,7 @@ SPDX-License-Identifier: LGPL-2.0-or-later
<file>qml/Sidebar.qml</file>
<file>qml/SlideshowManager.qml</file>
<file>qml/Tag.qml</file>
<file>qml/TagInput.qml</file>
<file>qml/ThumbnailStrip.qml</file>
<file>qml/VideoPlayer.qml</file>
</qresource>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment