Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 203 additions & 16 deletions qml/components/PropertiesSheet.qml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,166 @@ Item {
+ desktop.folderSizeResult.folders + " folders · "
+ desktop.folderSizeResult.links + " links"
}
readonly property string richPath: {
if (!root.visible || !root.details || root.details.path === undefined)
return ""
if (root.details.isDirectory === true || root.details.isSymlink === true)
return ""
return root.details.path || ""
}

visible: false
anchors.fill: parent
z: 1000

function addRow(rows, label, value) {
var text = value === undefined || value === null ? "" : String(value)
if (text !== "")
rows.push({ label: label, value: text })
}

function addSection(rows, label) {
rows.push({ section: true, label: label, value: "" })
}

function formatDuration(milliseconds) {
var total = Math.max(0, Math.round(Number(milliseconds) / 1000))
if (!isFinite(total) || total <= 0)
return ""
var hours = Math.floor(total / 3600)
var minutes = Math.floor((total % 3600) / 60)
var seconds = total % 60
function pad(value) { return value < 10 ? "0" + value : String(value) }
return hours > 0
? hours + ":" + pad(minutes) + ":" + pad(seconds)
: minutes + ":" + pad(seconds)
}

function formatBitRate(value) {
var bits = Number(value)
if (!isFinite(bits) || bits <= 0)
return ""
if (bits >= 1000000)
return (bits / 1000000).toFixed(bits < 10000000 ? 1 : 0) + " Mbps"
return Math.round(bits / 1000) + " kbps"
}

function imageCameraText() {
var parts = []
if (imageMetadata.cameraMake !== "")
parts.push(imageMetadata.cameraMake)
if (imageMetadata.cameraModel !== ""
&& imageMetadata.cameraModel !== imageMetadata.cameraMake)
parts.push(imageMetadata.cameraModel)
return parts.join(" · ")
}

function propertyRows() {
var rows = []
root.addSection(rows, "FILE")
root.addRow(rows, "TYPE", root.details.type || "")
root.addRow(rows, "MIME", root.details.mime || "")
root.addRow(rows, "LOCATION", root.details.parent || "")
root.addRow(rows, "PATH", root.details.path || "")
root.addRow(rows, "SIZE", root.displayedSize)
root.addRow(rows, "CONTENTS", root.folderContents)
root.addRow(rows, "MODIFIED", root.details.modified || "")
root.addRow(rows, "CREATED", root.details.created || "")
root.addRow(rows, "OWNER", root.details.owner || "")
root.addRow(rows, "GROUP", root.details.group || "")
root.addRow(rows, "PERMISSIONS", root.details.permissions || "")
root.addRow(rows, "LINK TARGET", root.details.symlinkTarget || "")

if (imageMetadata.active) {
root.addSection(rows, "IMAGE")
if (imageMetadata.loading) {
root.addRow(rows, "STATUS", "Reading metadata…")
} else if (imageMetadata.supported) {
if (imageMetadata.pixelWidth > 0 && imageMetadata.pixelHeight > 0)
root.addRow(rows, "DIMENSIONS", imageMetadata.pixelWidth + " × " + imageMetadata.pixelHeight + " px")
root.addRow(rows, "FORMAT", imageMetadata.formatName.toUpperCase())
if (imageMetadata.animated)
root.addRow(rows, "ANIMATION", imageMetadata.frameCount > 0
? imageMetadata.frameCount + " frames" : "Animated")
root.addRow(rows, "CAMERA", root.imageCameraText())
root.addRow(rows, "LENS", imageMetadata.lensModel)
root.addRow(rows, "DATE TAKEN", imageMetadata.dateTaken)
root.addRow(rows, "EXPOSURE", imageMetadata.exposureTime)
root.addRow(rows, "APERTURE", imageMetadata.aperture)
root.addRow(rows, "ISO", imageMetadata.iso)
root.addRow(rows, "FOCAL LENGTH", imageMetadata.focalLength)
root.addRow(rows, "ORIENTATION", imageMetadata.orientation)
root.addRow(rows, "EXPOSURE BIAS", imageMetadata.exposureBias)
root.addRow(rows, "WHITE BALANCE", imageMetadata.whiteBalance)
root.addRow(rows, "COLOR SPACE", imageMetadata.colorSpace)
root.addRow(rows, "SOFTWARE", imageMetadata.software)
root.addRow(rows, "ARTIST", imageMetadata.artist)
root.addRow(rows, "COPYRIGHT", imageMetadata.copyright)
if (imageMetadata.metadataLimited)
root.addRow(rows, "METADATA", "Deep metadata skipped for large image")
}
}

if (mediaMetadata.active) {
root.addSection(rows, "MEDIA")
if (mediaMetadata.loading) {
root.addRow(rows, "STATUS", "Reading metadata…")
} else if (mediaMetadata.supported) {
root.addRow(rows, "MEDIA KIND", mediaMetadata.mediaKind.toUpperCase())
root.addRow(rows, "CONTAINER", mediaMetadata.formatName)
root.addRow(rows, "DURATION", root.formatDuration(mediaMetadata.durationMs))
root.addRow(rows, "BIT RATE", root.formatBitRate(mediaMetadata.bitRate))
root.addRow(rows, "TITLE", mediaMetadata.title)
root.addRow(rows, "ARTIST", mediaMetadata.artist)
root.addRow(rows, "ALBUM", mediaMetadata.album)
if (mediaMetadata.hasAudio) {
root.addRow(rows, "AUDIO CODEC", mediaMetadata.audioCodec)
root.addRow(rows, "SAMPLE RATE", mediaMetadata.sampleRate > 0
? mediaMetadata.sampleRate + " Hz" : "")
root.addRow(rows, "CHANNELS", mediaMetadata.channels > 0
? mediaMetadata.channels + (mediaMetadata.channelLayout !== ""
? " · " + mediaMetadata.channelLayout : "") : mediaMetadata.channelLayout)
}
if (mediaMetadata.hasVideo) {
root.addRow(rows, "VIDEO CODEC", mediaMetadata.videoCodec)
if (mediaMetadata.videoWidth > 0 && mediaMetadata.videoHeight > 0)
root.addRow(rows, "VIDEO SIZE", mediaMetadata.videoWidth + " × " + mediaMetadata.videoHeight + " px")
root.addRow(rows, "FRAME RATE", mediaMetadata.frameRate > 0
? mediaMetadata.frameRate.toFixed(2).replace(/\.00$/, "") + " fps" : "")
}
}
}

if (fontMetadata.active) {
root.addSection(rows, "FONT")
if (fontMetadata.loading) {
root.addRow(rows, "STATUS", "Reading metadata…")
} else if (fontMetadata.supported) {
root.addRow(rows, "FAMILY", fontMetadata.familyName)
root.addRow(rows, "STYLE NAME", fontMetadata.styleName)
root.addRow(rows, "STYLE", fontMetadata.styleLabel)
root.addRow(rows, "WEIGHT", fontMetadata.weight > 0 ? fontMetadata.weight : "")
root.addRow(rows, "UNITS / EM", fontMetadata.unitsPerEm > 0
? Math.round(fontMetadata.unitsPerEm) : "")
root.addRow(rows, "WRITING SYSTEMS", fontMetadata.writingSystems)
}
}

if (pdfMetadata.active) {
root.addSection(rows, "PDF")
if (pdfMetadata.loading) {
root.addRow(rows, "STATUS", "Reading metadata…")
} else if (pdfMetadata.supported) {
root.addRow(rows, "PAGES", pdfMetadata.pageCount > 0 ? pdfMetadata.pageCount : "")
root.addRow(rows, "DOCUMENT TITLE", pdfMetadata.title)
root.addRow(rows, "AUTHOR", pdfMetadata.author)
root.addRow(rows, "SUBJECT", pdfMetadata.subject)
root.addRow(rows, "KEYWORDS", pdfMetadata.keywords)
}
}
return rows
}

function close() {
desktop.cancelFolderSize()
root.folderSizeError = ""
Expand All @@ -56,6 +211,33 @@ Item {
desktop.cancelFolderSize()
}

ImagePreviewLoader {
id: imageMetadata
active: root.richPath !== "" && imageMetadata.isCandidate(root.richPath)
path: active ? root.richPath : ""
}

MediaPreviewLoader {
id: mediaMetadata
metadataOnly: true
active: root.richPath !== "" && mediaMetadata.isCandidate(root.richPath)
path: active ? root.richPath : ""
}

FontPreviewLoader {
id: fontMetadata
metadataOnly: true
active: root.richPath !== "" && fontMetadata.isCandidate(root.richPath)
path: active ? root.richPath : ""
}

PdfPreviewLoader {
id: pdfMetadata
metadataOnly: true
active: root.richPath !== "" && pdfMetadata.isCandidate(root.richPath)
path: active ? root.richPath : ""
}

Rectangle {
anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.48)
Expand Down Expand Up @@ -120,34 +302,37 @@ Item {
height: parent.height - 154 * root.uiScale
clip: true
boundsBehavior: Flickable.StopAtBounds
model: [
{ label: "TYPE", value: root.details.type || "" },
{ label: "MIME", value: root.details.mime || "" },
{ label: "LOCATION", value: root.details.parent || "" },
{ label: "PATH", value: root.details.path || "" },
{ label: "SIZE", value: root.displayedSize },
{ label: "CONTENTS", value: root.folderContents },
{ label: "MODIFIED", value: root.details.modified || "" },
{ label: "CREATED", value: root.details.created || "" },
{ label: "OWNER", value: root.details.owner || "" },
{ label: "GROUP", value: root.details.group || "" },
{ label: "PERMISSIONS", value: root.details.permissions || "" },
{ label: "LINK TARGET", value: root.details.symlinkTarget || "" }
]
model: root.propertyRows()

delegate: Item {
id: propertyRow
required property var modelData

readonly property bool section: modelData.section === true
width: ListView.view.width
height: modelData.value !== "" ? 52 * root.uiScale : 0
visible: modelData.value !== ""
height: section ? 31 * root.uiScale
: (modelData.value !== "" ? 52 * root.uiScale : 0)
visible: section || modelData.value !== ""

Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: propertyRow.section ? 9 * root.uiScale : 7 * root.uiScale
visible: propertyRow.section
text: "// " + propertyRow.modelData.label
color: Ryoku.inkFaint
font.family: Ryoku.monoFont
font.pixelSize: 8 * root.uiScale
font.letterSpacing: 1.1
}

Text {
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 7 * root.uiScale
width: 110 * root.uiScale
visible: !propertyRow.section
text: propertyRow.modelData.label
color: Ryoku.inkMuted
font.family: Ryoku.monoFont
Expand All @@ -161,6 +346,7 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 7 * root.uiScale
visible: !propertyRow.section
text: propertyRow.modelData.value
elide: Text.ElideMiddle
color: Ryoku.ink
Expand All @@ -173,6 +359,7 @@ Item {
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
visible: !propertyRow.section
color: Ryoku.lineSoft
}
}
Expand Down
23 changes: 13 additions & 10 deletions src/preview/FontProbe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,19 @@ QJsonObject probe(
return {};
}

QJsonObject payload;
payload.insert(QStringLiteral("family"), rawFont.familyName());
payload.insert(QStringLiteral("styleName"), rawFont.styleName());
payload.insert(QStringLiteral("style"), styleLabel(rawFont.style()));
payload.insert(QStringLiteral("weight"), rawFont.weight());
payload.insert(QStringLiteral("unitsPerEm"), rawFont.unitsPerEm());
payload.insert(QStringLiteral("writingSystems"), detectedWritingSystems(rawFont));
payload.insert(QStringLiteral("fileSize"), QString::number(fileSize));

const bool renderSample = request.value(QStringLiteral("renderSample")).toBool(true);
if (!renderSample)
return payload;

QString firstLine = filterSupported(rawFont, QStringLiteral("Aa Bb Cc 0123456789"));
QString secondLine = filterSupported(rawFont, QStringLiteral("The quick brown fox"));
if (visibleCharacterCount(firstLine) < 4)
Expand Down Expand Up @@ -357,21 +370,11 @@ QJsonObject probe(
if (png.isEmpty())
return {};

const QJsonArray writingSystems = detectedWritingSystems(rawFont);

QJsonObject payload;
payload.insert(QStringLiteral("family"), rawFont.familyName());
payload.insert(QStringLiteral("styleName"), rawFont.styleName());
payload.insert(QStringLiteral("style"), styleLabel(rawFont.style()));
payload.insert(QStringLiteral("weight"), rawFont.weight());
payload.insert(QStringLiteral("unitsPerEm"), rawFont.unitsPerEm());
payload.insert(QStringLiteral("writingSystems"), writingSystems);
payload.insert(QStringLiteral("sampleText"), sampleLines.join(QChar::LineFeed));
payload.insert(QStringLiteral("sampleFormat"), QStringLiteral("png"));
payload.insert(QStringLiteral("sampleBase64"), QString::fromLatin1(png.toBase64()));
payload.insert(QStringLiteral("sampleWidth"), image.width());
payload.insert(QStringLiteral("sampleHeight"), image.height());
payload.insert(QStringLiteral("fileSize"), QString::number(fileSize));
return payload;
}

Expand Down
10 changes: 9 additions & 1 deletion src/preview/MediaPreviewWorkflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ void MediaPreviewLoader::setActive(bool active) {
scheduleLoad();
}

void MediaPreviewLoader::setMetadataOnly(bool metadataOnly) {
if (m_metadataOnly == metadataOnly)
return;
m_metadataOnly = metadataOnly;
emit metadataOnlyChanged();
scheduleLoad();
}

bool MediaPreviewLoader::isCandidate(const QString& path) const {
return isMediaCandidatePath(path);
}
Expand Down Expand Up @@ -145,7 +153,7 @@ void MediaPreviewLoader::startLoad() {
QJsonObject request;
request.insert(QStringLiteral("op"), QStringLiteral("media-probe"));
request.insert(QStringLiteral("path"), loadPath);
request.insert(QStringLiteral("poster"), true);
request.insert(QStringLiteral("poster"), !m_metadataOnly);
request.insert(QStringLiteral("maxWidth"), 1000);
request.insert(QStringLiteral("maxHeight"), 700);

Expand Down
5 changes: 5 additions & 0 deletions src/preview/MediaPreviewWorkflow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class MediaPreviewLoader : public QObject {

Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(bool metadataOnly READ metadataOnly WRITE setMetadataOnly NOTIFY metadataOnlyChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(bool supported READ supported NOTIFY resultChanged)
Q_PROPERTY(bool hasAudio READ hasAudio NOTIFY resultChanged)
Expand Down Expand Up @@ -47,6 +48,8 @@ class MediaPreviewLoader : public QObject {
void setPath(const QString& path);
bool active() const { return m_active; }
void setActive(bool active);
bool metadataOnly() const { return m_metadataOnly; }
void setMetadataOnly(bool metadataOnly);

bool loading() const { return m_loading; }
bool supported() const { return m_supported; }
Expand Down Expand Up @@ -75,6 +78,7 @@ class MediaPreviewLoader : public QObject {
signals:
void pathChanged();
void activeChanged();
void metadataOnlyChanged();
void loadingChanged();
void resultChanged();

Expand All @@ -86,6 +90,7 @@ class MediaPreviewLoader : public QObject {

QString m_path;
bool m_active = false;
bool m_metadataOnly = false;
bool m_loading = false;
bool m_supported = false;
bool m_hasAudio = false;
Expand Down
Loading
Loading