Projectorsize selection with slider

This commit is contained in:
FinnStutzenstein 2017-06-21 13:09:20 +02:00
parent 029366de5f
commit 2d35b9e371
7 changed files with 137 additions and 28 deletions

View File

@ -52,6 +52,7 @@ Core:
- Improved reconnect handling if the server was flushed [#3297]. - Improved reconnect handling if the server was flushed [#3297].
- Highlight list entries in a light blue, if a related object is projected (e.g. - Highlight list entries in a light blue, if a related object is projected (e.g.
a list of speakers of a motion) [#3301] a list of speakers of a motion) [#3301]
- Select the projector resolution with a slider and an aspect ratio [#3311].
Mediafiles: Mediafiles:
- Fixed reloading of PDF on page change [#3274]. - Fixed reloading of PDF on page change [#3274].

View File

@ -185,6 +185,7 @@ OpenSlides uses the following projects or parts of them:
* `angular-ui-router <http://angular-ui.github.io/ui-router/>`_, License: MIT * `angular-ui-router <http://angular-ui.github.io/ui-router/>`_, License: MIT
* `angular-ui-tree <https://github.com/angular-ui-tree/angular-ui-tree>`_, License: MIT * `angular-ui-tree <https://github.com/angular-ui-tree/angular-ui-tree>`_, License: MIT
* `angular-xeditable <https://github.com/vitalets/angular-xeditable>`_, License: MIT * `angular-xeditable <https://github.com/vitalets/angular-xeditable>`_, License: MIT
* `angularjs-slider <https://github.com/angular-slider/angularjs-slider>`_, License: MIT
* `api-check <https://github.com/kentcdodds/api-check>`_, License: MIT * `api-check <https://github.com/kentcdodds/api-check>`_, License: MIT
* `bootstrap <http://getbootstrap.com>`_, License: MIT * `bootstrap <http://getbootstrap.com>`_, License: MIT
* `bootstrap-ui-datetime-picker <https://github.com/Gillardo/bootstrap-ui-datetime-picker>`_, License: MIT * `bootstrap-ui-datetime-picker <https://github.com/Gillardo/bootstrap-ui-datetime-picker>`_, License: MIT

View File

@ -20,6 +20,7 @@
"angular-ui-router": "~0.3.1", "angular-ui-router": "~0.3.1",
"angular-ui-tree": "~2.22.0", "angular-ui-tree": "~2.22.0",
"angular-xeditable": "~0.5.0", "angular-xeditable": "~0.5.0",
"angularjs-slider": "~6.2.2",
"bootstrap-css-only": "~3.3.6", "bootstrap-css-only": "~3.3.6",
"bootstrap-ui-datetime-picker": "~2.4.0", "bootstrap-ui-datetime-picker": "~2.4.0",
"ckeditor": "4.6.1", "ckeditor": "4.6.1",

View File

@ -1444,6 +1444,31 @@ img {
font-size: 85%; font-size: 85%;
} }
/* Custom OpenSlides slider classes */
.os-slider {
margin-top: 40px;
margin-bottom: 12px;
}
.os-slider.rzslider .rz-bar {
background: #317796;
opacity: 0.3;
height: 2px;
}
.os-slider.rzslider .rz-selection {
background: #317796;
}
.os-slider.rzslider .rz-pointer {
width: 14px;
height: 14px;
top: -7px;
bottom: 0;
background-color: #317796;
border-radius: 7px;
}
.os-slider.rzslider .rz-pointer:after {
display: none;
}
/* ngDialog: override ngdialog-theme-default */ /* ngDialog: override ngdialog-theme-default */
.ngdialog.ngdialog-theme-default { .ngdialog.ngdialog-theme-default {

View File

@ -20,6 +20,7 @@ angular.module('OpenSlidesApp.core.site', [
'ckeditor', 'ckeditor',
'luegg.directives', 'luegg.directives',
'xeditable', 'xeditable',
'rzModule',
]) ])
// Can be used to find out if the projector or the side is used // Can be used to find out if the projector or the side is used
@ -1428,6 +1429,20 @@ angular.module('OpenSlidesApp.core.site', [
ProjectorMessage, ngDialog) { ProjectorMessage, ngDialog) {
ProjectionDefault.bindAll({}, $scope, 'projectiondefaults'); ProjectionDefault.bindAll({}, $scope, 'projectiondefaults');
/* Info on resolution calculating:
* Internally the resolution is saved as (width, height) but the user has
* an aspect ratio to choose and a width from 800 to 3840 (4K).*/
$scope.aspectRatios = {
'4:3': 4/3,
'16:9': 16/9,
'16:10': 16/10,
};
// when converting (x,y) -> (ratio, percentage) round issues may occur
// (e.g. 800/600 != 4/3 with internal calculation issues). With this environment
// is tested, if the calculated value is in the following interval:
// [expected-environment; expected+environment]
var RATIO_ENVIRONMENT = 0.05;
// watch for changes in projector_broadcast // watch for changes in projector_broadcast
// and projector_currentListOfSpeakers_reference // and projector_currentListOfSpeakers_reference
var last_broadcast, last_clos; var last_broadcast, last_clos;
@ -1448,8 +1463,9 @@ angular.module('OpenSlidesApp.core.site', [
// watch for changes in Projector, and recalc scale and iframeHeight // watch for changes in Projector, and recalc scale and iframeHeight
var first_watch = true; var first_watch = true;
$scope.resolutions = []; $scope.resolutions = {};
$scope.edit = []; $scope.edit = [];
$scope.sliders = {};
$scope.$watch(function () { $scope.$watch(function () {
return Projector.lastModified(); return Projector.lastModified();
}, function () { }, function () {
@ -1463,6 +1479,25 @@ angular.module('OpenSlidesApp.core.site', [
height: projector.height height: projector.height
}; };
$scope.edit[projector.id] = false; $scope.edit[projector.id] = false;
$scope.sliders[projector.id] = {
value: projector.width,
options: {
id: projector.id,
floor: 800,
ceil: 3840,
translate: function (value) {
return value + 'px';
},
onChange: function (v) {
$scope.calcResolution(projector);
},
onEnd: function (v) {
$scope.saveResolution(projector);
},
hideLimitLabels: true,
},
};
$scope.setAspectRatio(projector, $scope.getAspectRatio(projector));
} }
}); });
if ($scope.projectors.length) { if ($scope.projectors.length) {
@ -1470,6 +1505,36 @@ angular.module('OpenSlidesApp.core.site', [
} }
}); });
$scope.getAspectRatio = function (projector) {
var ratio = projector.width/projector.height;
var foundRatio = _.findKey($scope.aspectRatios, function (value) {
return value >= (ratio-RATIO_ENVIRONMENT) && value <= (ratio+RATIO_ENVIRONMENT);
});
if (foundRatio === undefined) {
return _.keys($scope.aspectRatios)[0];
} else {
return foundRatio;
}
};
$scope.setAspectRatio = function (projector, aspectRatio) {
$scope.resolutions[projector.id].aspectRatio = aspectRatio;
$scope.resolutions[projector.id].aspectRatioNumber = $scope.aspectRatios[aspectRatio];
$scope.calcResolution(projector);
};
$scope.calcResolution = function (projector) {
var ratio = $scope.resolutions[projector.id].aspectRatioNumber;
var width = $scope.sliders[projector.id].value;
$scope.resolutions[projector.id].width = width;
$scope.resolutions[projector.id].height = Math.round(width/ratio);
};
$scope.toggleEditMenu = function (projectorId) {
$scope.edit[projectorId] = !$scope.edit[projectorId];
$timeout(function () {
$scope.$broadcast('rzSliderForceRender');
});
};
// Set list of speakers reference // Set list of speakers reference
$scope.setListOfSpeakers = function (projector) { $scope.setListOfSpeakers = function (projector) {
Config.get('projector_currentListOfSpeakers_reference').value = projector.id; Config.get('projector_currentListOfSpeakers_reference').value = projector.id;
@ -1521,14 +1586,18 @@ angular.module('OpenSlidesApp.core.site', [
projector.config = projector.elements; projector.config = projector.elements;
Projector.save(projector); Projector.save(projector);
}; };
$scope.changeResolution = function (projectorId) { $scope.saveResolution = function (projector) {
$http.post( $http.post(
'/rest/core/projector/' + projectorId + '/set_resolution/', '/rest/core/projector/' + projector.id + '/set_resolution/',
$scope.resolutions[projectorId] $scope.resolutions[projector.id]
).then(function (success) { ).then(function (success) {
$scope.resolutions[projectorId].error = null; $scope.resolutions[projector.id].error = null;
}, function (error) { }, function (error) {
$scope.resolutions[projectorId].error = error.data.detail; if (error.data) {
$scope.resolutions[projector.id].error = error.data.detail;
} else {
$scope.resolutions[projector.id].error = null;
}
}); });
}; };

View File

@ -45,21 +45,22 @@
{{ projector.id }}: {{ projector.id }}:
<strong>{{ projector.name | translate }}</strong> <strong>{{ projector.name | translate }}</strong>
</a> </a>
<a href="" class="pull-right" ng-click="edit[projector.id] = !edit[projector.id]"><i class="fa" ng-class="edit[projector.id] ? 'fa-times' : 'fa-pencil'"></i></a> <a href="" class="pull-right" ng-click="toggleEditMenu(projector.id)"><i class="fa" ng-class="edit[projector.id] ? 'fa-times' : 'fa-pencil'"></i></a>
</div> </div>
<div ng-show="edit[projector.id]" style="margin-bottom: -20px;"> <div ng-show="edit[projector.id]" style="margin-bottom: -20px;">
<div> <div>
<div class="dropdown" uib-dropdown> <div class="dropdown" uib-dropdown>
<button class="btn btn-default btn-sm" id="menuProjector{{ pr.id }}" uib-dropdown-toggle> <button class="btn btn-default btn-sm" id="menuProjector{{ projector.id }}" uib-dropdown-toggle>
<translate>Projection defaults</translate> <translate>Projection defaults</translate>
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu dropdown-entries" aria-labelledby="menuProjector{{ pr.id }}"> <ul class="dropdown-menu" uib-dropdown-menu aria-labelledby="menuProjector{{ projector.id }}">
<li ng-repeat="projectiondefault in projectiondefaults | orderBy:'id'" <li ng-repeat="projectiondefault in projectiondefaults | orderBy:'id'">
ng-click="setProjectionDefault(projector, projectiondefault)"> <a href ng-click="setProjectionDefault(projector, projectiondefault)">
<i class="fa fa-check" ng-if="projectiondefault.projector_id === projector.id"></i> <i class="fa fa-check" ng-if="projectiondefault.projector_id === projector.id"></i>
{{ projectiondefault.display_name | translate }} {{ projectiondefault.display_name | translate }}
</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -82,19 +83,30 @@
<div> <div>
<label for="resolution{{ projector.id }}" class="control-label"><translate>Resolution</translate>:</label> <label for="resolution{{ projector.id }}" class="control-label"><translate>Resolution</translate>:</label>
<div id="resolution{{ projector.id }}"> <div id="resolution{{ projector.id }}">
<input ng-model="resolutions[projector.id].width" <div>
ng-model-option="{debounce: 2000}" <span>
ng-change="changeResolution(projector.id)" {{ resolutions[projector.id].width }}&times;{{ resolutions[projector.id].height }}
class="form-control resolution" </span>
id="{{ projector.id }}_width" <div class="dropdown" uib-dropdown>
type="number"> <button class="btn btn-default btn-sm" id="aspectRatio{{ projector.id }}" uib-dropdown-toggle>
x <translate>Aspect ratio</translate>: {{ resolutions[projector.id].aspectRatio }}
<input ng-model="resolutions[projector.id].height" <span class="caret"></span>
ng-model-option="{debounce: 2000}" </button>
ng-change="changeResolution(projector.id)" <ul class="dropdown-menu" uib-dropdown-menu aria-labelledby="aspectRatio{{ projector.id }}">
class="form-control resolution" <li ng-repeat="(aspectRatio, value) in aspectRatios track by $index">
id="{{ projector.id }}_height" <a href ng-click="setAspectRatio(projector, aspectRatio); saveResolution(projector);">
type="number"> <i class="fa fa-check" ng-if="aspectRatio === resolutions[projector.id].aspectRatio"></i>
{{ aspectRatio }}
</a>
</li>
</ul>
</div>
</div>
<div>
<rzslider class="os-slider"
rz-slider-model="sliders[projector.id].value"
rz-slider-options="sliders[projector.id].options"></rzslider>
</div>
</div> </div>
<p class="help-block"> <p class="help-block">
{{ resolutions[projector.id].error }} {{ resolutions[projector.id].error }}

View File

@ -404,8 +404,8 @@ class ProjectorViewSet(ModelViewSet):
if not isinstance(request.data['width'], int) or not isinstance(request.data['height'], int): if not isinstance(request.data['width'], int) or not isinstance(request.data['height'], int):
raise ValidationError({'detail': 'Data has to be integers.'}) raise ValidationError({'detail': 'Data has to be integers.'})
if (request.data['width'] < 800 or request.data['width'] > 3840 or if (request.data['width'] < 800 or request.data['width'] > 3840 or
request.data['height'] < 600 or request.data['height'] > 2160): request.data['height'] < 340 or request.data['height'] > 2880):
raise ValidationError({'detail': 'The Resolution have to be between 800x600 and 3840x2160.'}) raise ValidationError({'detail': 'The Resolution have to be between 800x340 and 3840x2880.'})
projector_instance = self.get_object() projector_instance = self.get_object()
projector_instance.width = request.data['width'] projector_instance.width = request.data['width']