diff --git a/src/components/Images.vue b/src/components/Images.vue
index dd07e1441..1b4a161b2 100644
--- a/src/components/Images.vue
+++ b/src/components/Images.vue
@@ -104,6 +104,10 @@ export default {
type: Boolean,
default: false,
},
+ rotation: {
+ type: Number,
+ default: 0,
+ },
},
data() {
return {
@@ -134,10 +138,12 @@ export default {
return this.basename
},
imgStyle() {
+ const transform = this.rotation !== 0 ? `rotate(${this.rotation}deg)` : undefined
if (this.zoomRatio === 1) {
return {
height: this.zoomHeight + 'px',
width: this.zoomWidth + 'px',
+ transform,
}
}
return {
@@ -145,6 +151,7 @@ export default {
marginLeft: Math.round(this.shiftX * 2) + 'px',
height: this.zoomHeight + 'px',
width: this.zoomWidth + 'px',
+ transform,
}
},
livePhoto() {
@@ -200,6 +207,9 @@ export default {
},
},
watch: {
+ rotation() {
+ this.resetZoom()
+ },
active(val, old) {
// the item was hidden before and is now the current view
if (val === true && old === false) {
diff --git a/src/views/Viewer.vue b/src/views/Viewer.vue
index 2dd198cd8..6c7c16f13 100644
--- a/src/views/Viewer.vue
+++ b/src/views/Viewer.vue
@@ -41,7 +41,7 @@
:enable-swipe="canSwipe && !editing"
:has-next="hasNext"
:has-previous="hasPrevious"
- :inline-actions="canEdit ? 1 : 0"
+ :inline-actions="(canEdit ? 1 : 0) + (canRotate ? 2 : 0)"
:spread-navigation="true"
:style="{ width: isSidebarShown ? `${sidebarPosition}px` : null }"
:name="currentFile.basename"
@@ -61,6 +61,24 @@
{{ t('viewer', 'Edit') }}
+
+
+
+
+ {{ t('viewer', 'Rotate counterclockwise') }}
+
+
+
+
+
+ {{ t('viewer', 'Rotate clockwise') }}
+
@@ -148,6 +166,7 @@
:is-full-screen="isFullscreen"
:is-sidebar-shown="isSidebarShown"
:loaded.sync="currentFile.loaded"
+ :rotation="rotation"
class="viewer__file viewer__file--active"
@update:editing="toggleEditor"
@error="currentFailed" />
@@ -207,6 +226,8 @@ import Fullscreen from 'vue-material-design-icons/Fullscreen.vue'
import FullscreenExit from 'vue-material-design-icons/FullscreenExit.vue'
import Pencil from 'vue-material-design-icons/PencilOutline.vue'
import DockRight from 'vue-material-design-icons/DockRight.vue'
+import RotateLeft from 'vue-material-design-icons/RotateLeft.vue'
+import RotateRight from 'vue-material-design-icons/RotateRight.vue'
import '@nextcloud/dialogs/style.css'
@@ -229,6 +250,8 @@ export default defineComponent({
NcActionLink,
NcModal,
Pencil,
+ RotateLeft,
+ RotateRight,
},
mixins: [isFullscreen, isMobile],
@@ -251,6 +274,7 @@ export default defineComponent({
comparisonFile: null,
nextFile: {},
fileList: [],
+ rotation: 0,
sortingConfig: null,
// States
@@ -333,6 +357,10 @@ export default defineComponent({
return ['image/jpeg', 'image/png', 'image/webp'].includes(this.currentFile?.mime)
},
+ canRotate() {
+ return this.isImage && !this.comparisonFile
+ },
+
sidebarOpenFilePath() {
try {
const relativePath = this.currentFile?.davPath?.split(defaultRootPath)[1]
@@ -1062,7 +1090,16 @@ export default defineComponent({
/**
* Open previous available file
*/
+ rotateLeft() {
+ this.rotation = ((this.rotation - 90) + 360) % 360
+ },
+
+ rotateRight() {
+ this.rotation = (this.rotation + 90) % 360
+ },
+
previous() {
+ this.rotation = 0
this.currentIndex--
if (this.currentIndex < 0) {
this.currentIndex = this.fileList.length - 1
@@ -1078,6 +1115,7 @@ export default defineComponent({
* Open next available file
*/
next() {
+ this.rotation = 0
this.currentIndex++
if (this.currentIndex > this.fileList.length - 1) {
this.currentIndex = 0