Merge pull request #18632 from owncloud/sidebad-image-preview
larger preview for images in the sidebar
This commit is contained in:
commit
4539377113
|
@ -19,6 +19,27 @@
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#app-sidebar .thumbnailContainer.image {
|
||||||
|
margin-left: -15px;
|
||||||
|
margin-right: -35px; /* 15 + 20 for the close button */
|
||||||
|
margin-top: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-sidebar .image .thumbnail {
|
||||||
|
width:100%;
|
||||||
|
display:block;
|
||||||
|
height: 250px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50% top;
|
||||||
|
background-size: 100%;
|
||||||
|
float: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-sidebar .image.portrait .thumbnail {
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
#app-sidebar .thumbnail {
|
#app-sidebar .thumbnail {
|
||||||
width: 75px;
|
width: 75px;
|
||||||
height: 75px;
|
height: 75px;
|
||||||
|
|
|
@ -52,6 +52,15 @@
|
||||||
return this.get('mimetype') === 'httpd/unix-directory';
|
return this.get('mimetype') === 'httpd/unix-directory';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this file is an image
|
||||||
|
*
|
||||||
|
* @return {boolean} true if this is an image, false otherwise
|
||||||
|
*/
|
||||||
|
isImage: function() {
|
||||||
|
return this.get('mimetype').substr(0, 6) === 'image/';
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the full path to this file
|
* Returns the full path to this file
|
||||||
*
|
*
|
||||||
|
|
|
@ -1359,6 +1359,12 @@
|
||||||
if (options.y) {
|
if (options.y) {
|
||||||
urlSpec.y = options.y;
|
urlSpec.y = options.y;
|
||||||
}
|
}
|
||||||
|
if (options.a) {
|
||||||
|
urlSpec.a = options.a;
|
||||||
|
}
|
||||||
|
if (options.mode) {
|
||||||
|
urlSpec.mode = options.mode;
|
||||||
|
}
|
||||||
|
|
||||||
if (etag){
|
if (etag){
|
||||||
// use etag as cache buster
|
// use etag as cache buster
|
||||||
|
@ -1377,9 +1383,14 @@
|
||||||
img.onload = function(){
|
img.onload = function(){
|
||||||
// if loading the preview image failed (no preview for the mimetype) then img.width will < 5
|
// if loading the preview image failed (no preview for the mimetype) then img.width will < 5
|
||||||
if (img.width > 5) {
|
if (img.width > 5) {
|
||||||
ready(previewURL);
|
ready(previewURL, img);
|
||||||
|
} else if (options.error) {
|
||||||
|
options.error();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (options.error) {
|
||||||
|
img.onerror = options.error;
|
||||||
|
}
|
||||||
img.src = previewURL;
|
img.src = previewURL;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var TEMPLATE =
|
var TEMPLATE =
|
||||||
'<a href="#" class="thumbnail action-default"></a>' +
|
'<div class="thumbnailContainer"><a href="#" class="thumbnail action-default"></a></div>' +
|
||||||
'<div class="file-details-container">' +
|
'<div class="file-details-container">' +
|
||||||
'<div class="fileName"><h3 title="{{name}}" class="ellipsis">{{name}}</h3></div>' +
|
'<div class="fileName"><h3 title="{{name}}" class="ellipsis">{{name}}</h3></div>' +
|
||||||
' <div class="file-details ellipsis">' +
|
' <div class="file-details ellipsis">' +
|
||||||
|
@ -106,6 +106,7 @@
|
||||||
if (this.model) {
|
if (this.model) {
|
||||||
var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
|
var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
|
||||||
this.$el.html(this.template({
|
this.$el.html(this.template({
|
||||||
|
type: this.model.isImage()? 'image': '',
|
||||||
nameLabel: t('files', 'Name'),
|
nameLabel: t('files', 'Name'),
|
||||||
name: this.model.get('displayName') || this.model.get('name'),
|
name: this.model.get('displayName') || this.model.get('name'),
|
||||||
pathLabel: t('files', 'Path'),
|
pathLabel: t('files', 'Path'),
|
||||||
|
@ -123,16 +124,51 @@
|
||||||
|
|
||||||
// TODO: we really need OC.Previews
|
// TODO: we really need OC.Previews
|
||||||
var $iconDiv = this.$el.find('.thumbnail');
|
var $iconDiv = this.$el.find('.thumbnail');
|
||||||
|
$iconDiv.addClass('icon-loading');
|
||||||
|
$container = this.$el.find('.thumbnailContainer');
|
||||||
if (!this.model.isDirectory()) {
|
if (!this.model.isDirectory()) {
|
||||||
this._fileList.lazyLoadPreview({
|
this._fileList.lazyLoadPreview({
|
||||||
path: this.model.getFullPath(),
|
path: this.model.getFullPath(),
|
||||||
mime: this.model.get('mimetype'),
|
mime: this.model.get('mimetype'),
|
||||||
etag: this.model.get('etag'),
|
etag: this.model.get('etag'),
|
||||||
x: 75,
|
y: this.model.isImage() ? 250: 75,
|
||||||
y: 75,
|
x: this.model.isImage() ? 99999 /* only limit on y */ : 75,
|
||||||
callback: function(previewUrl) {
|
a: this.model.isImage() ? 1 : null,
|
||||||
$iconDiv.css('background-image', 'url("' + previewUrl + '")');
|
callback: function(previewUrl, img) {
|
||||||
|
$iconDiv.previewImg = previewUrl;
|
||||||
|
if (img) {
|
||||||
|
$iconDiv.removeClass('icon-loading');
|
||||||
|
if(img.height > img.width) {
|
||||||
|
$container.addClass('portrait');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (this.model.isImage() && img) {
|
||||||
|
$iconDiv.parent().addClass('image');
|
||||||
|
var targetHeight = img.height / window.devicePixelRatio;
|
||||||
|
if (targetHeight <= 75) {
|
||||||
|
$container.removeClass('image'); // small enough to fit in normaly
|
||||||
|
targetHeight = 75;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetHeight = 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only set background when we have an actual preview
|
||||||
|
// when we dont have a preview we show the mime icon in the error handler
|
||||||
|
if (img) {
|
||||||
|
$iconDiv.css({
|
||||||
|
'background-image': 'url("' + previewUrl + '")',
|
||||||
|
'height': targetHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.bind(this),
|
||||||
|
error: function() {
|
||||||
|
$iconDiv.removeClass('icon-loading');
|
||||||
|
this.$el.find('.thumbnailContainer').removeClass('image'); //fall back to regular view
|
||||||
|
$iconDiv.css({
|
||||||
|
'background-image': 'url("' + $iconDiv.previewImg + '")'
|
||||||
|
});
|
||||||
|
}.bind(this)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// TODO: special icons / shared / external
|
// TODO: special icons / shared / external
|
||||||
|
|
|
@ -31,6 +31,7 @@ $maxY = array_key_exists('y', $_GET) ? (int)$_GET['y'] : '36';
|
||||||
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool)$_GET['scalingup'] : true;
|
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool)$_GET['scalingup'] : true;
|
||||||
$keepAspect = array_key_exists('a', $_GET) ? true : false;
|
$keepAspect = array_key_exists('a', $_GET) ? true : false;
|
||||||
$always = array_key_exists('forceIcon', $_GET) ? (bool)$_GET['forceIcon'] : true;
|
$always = array_key_exists('forceIcon', $_GET) ? (bool)$_GET['forceIcon'] : true;
|
||||||
|
$mode = array_key_exists('mode', $_GET) ? $_GET['mode'] : 'fill';
|
||||||
|
|
||||||
if ($file === '') {
|
if ($file === '') {
|
||||||
//400 Bad Request
|
//400 Bad Request
|
||||||
|
@ -56,6 +57,7 @@ if (!$info instanceof OCP\Files\FileInfo || !$always && !\OC::$server->getPrevie
|
||||||
$preview->setMaxX($maxX);
|
$preview->setMaxX($maxX);
|
||||||
$preview->setMaxY($maxY);
|
$preview->setMaxY($maxY);
|
||||||
$preview->setScalingUp($scalingUp);
|
$preview->setScalingUp($scalingUp);
|
||||||
|
$preview->setMode($mode);
|
||||||
$preview->setKeepAspect($keepAspect);
|
$preview->setKeepAspect($keepAspect);
|
||||||
$preview->showPreview();
|
$preview->showPreview();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ class Preview {
|
||||||
//the thumbnail folder
|
//the thumbnail folder
|
||||||
const THUMBNAILS_FOLDER = 'thumbnails';
|
const THUMBNAILS_FOLDER = 'thumbnails';
|
||||||
|
|
||||||
|
const MODE_FILL = 'fill';
|
||||||
|
const MODE_COVER = 'cover';
|
||||||
|
|
||||||
//config
|
//config
|
||||||
private $maxScaleFactor;
|
private $maxScaleFactor;
|
||||||
/** @var int maximum width allowed for a preview */
|
/** @var int maximum width allowed for a preview */
|
||||||
|
@ -56,6 +59,7 @@ class Preview {
|
||||||
private $scalingUp;
|
private $scalingUp;
|
||||||
private $mimeType;
|
private $mimeType;
|
||||||
private $keepAspect = false;
|
private $keepAspect = false;
|
||||||
|
private $mode = self::MODE_FILL;
|
||||||
|
|
||||||
//used to calculate the size of the preview to generate
|
//used to calculate the size of the preview to generate
|
||||||
/** @var int $maxPreviewWidth max width a preview can have */
|
/** @var int $maxPreviewWidth max width a preview can have */
|
||||||
|
@ -331,6 +335,19 @@ class Preview {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to cover or fill the specified dimensions
|
||||||
|
*
|
||||||
|
* @param string $mode
|
||||||
|
*
|
||||||
|
* @return \OC\Preview
|
||||||
|
*/
|
||||||
|
public function setMode($mode) {
|
||||||
|
$this->mode = $mode;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether we need to generate a preview which keeps the aspect ratio of the original file
|
* Sets whether we need to generate a preview which keeps the aspect ratio of the original file
|
||||||
*
|
*
|
||||||
|
@ -531,14 +548,22 @@ class Preview {
|
||||||
* @param int $askedWidth
|
* @param int $askedWidth
|
||||||
* @param int $askedHeight
|
* @param int $askedHeight
|
||||||
*
|
*
|
||||||
|
* @param int $originalWidth
|
||||||
|
* @param int $originalHeight
|
||||||
* @return \int[]
|
* @return \int[]
|
||||||
*/
|
*/
|
||||||
private function applyAspectRatio($askedWidth, $askedHeight) {
|
private function applyAspectRatio($askedWidth, $askedHeight, $originalWidth = 0, $originalHeight = 0) {
|
||||||
$originalRatio = $this->maxPreviewWidth / $this->maxPreviewHeight;
|
if(!$originalWidth){
|
||||||
|
$originalWidth= $this->maxPreviewWidth;
|
||||||
|
}
|
||||||
|
if (!$originalHeight) {
|
||||||
|
$originalHeight = $this->maxPreviewHeight;
|
||||||
|
}
|
||||||
|
$originalRatio = $originalWidth / $originalHeight;
|
||||||
// Defines the box in which the preview has to fit
|
// Defines the box in which the preview has to fit
|
||||||
$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
|
$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
|
||||||
$askedWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor);
|
$askedWidth = min($askedWidth, $originalWidth * $scaleFactor);
|
||||||
$askedHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor);
|
$askedHeight = min($askedHeight, $originalHeight * $scaleFactor);
|
||||||
|
|
||||||
if ($askedWidth / $originalRatio < $askedHeight) {
|
if ($askedWidth / $originalRatio < $askedHeight) {
|
||||||
// width restricted
|
// width restricted
|
||||||
|
@ -550,6 +575,32 @@ class Preview {
|
||||||
return [(int)$askedWidth, (int)$askedHeight];
|
return [(int)$askedWidth, (int)$askedHeight];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the boundaries to cover the area
|
||||||
|
*
|
||||||
|
* @param int $askedWidth
|
||||||
|
* @param int $askedHeight
|
||||||
|
* @param int $previewWidth
|
||||||
|
* @param int $previewHeight
|
||||||
|
* @return \int[]
|
||||||
|
*/
|
||||||
|
private function applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight) {
|
||||||
|
$originalRatio = $previewWidth / $previewHeight;
|
||||||
|
// Defines the box in which the preview has to fit
|
||||||
|
$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
|
||||||
|
$askedWidth = min($askedWidth, $previewWidth * $scaleFactor);
|
||||||
|
$askedHeight = min($askedHeight, $previewHeight * $scaleFactor);
|
||||||
|
|
||||||
|
if ($askedWidth / $originalRatio > $askedHeight) {
|
||||||
|
// height restricted
|
||||||
|
$askedHeight = round($askedWidth / $originalRatio);
|
||||||
|
} else {
|
||||||
|
$askedWidth = round($askedHeight * $originalRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [(int)$askedWidth, (int)$askedHeight];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the
|
* Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the
|
||||||
* config
|
* config
|
||||||
|
@ -791,7 +842,15 @@ class Preview {
|
||||||
*/
|
*/
|
||||||
if ($this->keepAspect) {
|
if ($this->keepAspect) {
|
||||||
list($askedWidth, $askedHeight) =
|
list($askedWidth, $askedHeight) =
|
||||||
$this->applyAspectRatio($askedWidth, $askedHeight);
|
$this->applyAspectRatio($askedWidth, $askedHeight, $previewWidth, $previewHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->mode === self::MODE_COVER) {
|
||||||
|
list($scaleWidth, $scaleHeight) =
|
||||||
|
$this->applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight);
|
||||||
|
} else {
|
||||||
|
$scaleWidth = $askedWidth;
|
||||||
|
$scaleHeight = $askedHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -799,7 +858,7 @@ class Preview {
|
||||||
* Takes the scaling ratio into consideration
|
* Takes the scaling ratio into consideration
|
||||||
*/
|
*/
|
||||||
list($newPreviewWidth, $newPreviewHeight) = $this->scale(
|
list($newPreviewWidth, $newPreviewHeight) = $this->scale(
|
||||||
$image, $askedWidth, $askedHeight, $previewWidth, $previewHeight
|
$image, $scaleWidth, $scaleHeight, $previewWidth, $previewHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
// The preview has been resized and should now have the asked dimensions
|
// The preview has been resized and should now have the asked dimensions
|
||||||
|
@ -1000,6 +1059,9 @@ class Preview {
|
||||||
if ($this->keepAspect && !$isMaxPreview) {
|
if ($this->keepAspect && !$isMaxPreview) {
|
||||||
$previewPath .= '-with-aspect';
|
$previewPath .= '-with-aspect';
|
||||||
}
|
}
|
||||||
|
if ($this->mode === self::MODE_COVER) {
|
||||||
|
$previewPath .= '-cover';
|
||||||
|
}
|
||||||
$previewPath .= '.png';
|
$previewPath .= '.png';
|
||||||
|
|
||||||
return $previewPath;
|
return $previewPath;
|
||||||
|
|
Loading…
Reference in New Issue