Fix xeokit keyboard inputs and show icon when viewer has focus

pull/8256/head
Oliver Günther 5 years ago
parent 03e0269450
commit 09648e74f4
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 271
      app/assets/fonts/openproject_icon/openproject-icon-font.svg
  2. BIN
      app/assets/fonts/openproject_icon/openproject-icon-font.ttf
  3. BIN
      app/assets/fonts/openproject_icon/openproject-icon-font.woff
  4. BIN
      app/assets/fonts/openproject_icon/openproject-icon-font.woff2
  5. 542
      app/assets/stylesheets/fonts/_openproject_icon_definitions.scss
  6. 1
      app/assets/stylesheets/fonts/_openproject_icon_font.lsg
  7. 68
      frontend/npm-shrinkwrap.json
  8. 48
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.component.html
  9. 42
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.component.sass
  10. 41
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts
  11. 4
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts
  12. 29
      modules/bim/app/assets/stylesheets/bim/ifc_viewer/generic.sass
  13. 1
      modules/bim/config/locales/js-en.yml
  14. 1
      vendor/openproject-icon-font/src/input-disabled.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 144 KiB

@ -123,6 +123,7 @@
<li><span class="icon icon-import"></span>import</li>
<li><span class="icon icon-info1"></span>info1</li>
<li><span class="icon icon-info2"></span>info2</li>
<li><span class="icon icon-input-disabled"></span>input-disabled</li>
<li><span class="icon icon-installation-services"></span>installation-services</li>
<li><span class="icon icon-italic"></span>italic</li>
<li><span class="icon icon-key"></span>key</li>

@ -4532,7 +4532,7 @@
},
"camelcase-keys": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
"integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
"requires": {
"camelcase": "^2.0.0",
@ -4621,7 +4621,7 @@
"dependencies": {
"color-convert": {
"version": "0.5.3",
"resolved": "http://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
}
}
@ -4868,7 +4868,7 @@
},
"colors": {
"version": "1.1.2",
"resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
@ -5536,7 +5536,7 @@
},
"d": {
"version": "0.1.1",
"resolved": "http://registry.npmjs.org/d/-/d-0.1.1.tgz",
"resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz",
"integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=",
"requires": {
"es5-ext": "~0.10.2"
@ -6050,7 +6050,7 @@
},
"engine.io-client": {
"version": "3.2.1",
"resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
"integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
"dev": true,
"requires": {
@ -6183,7 +6183,7 @@
"dependencies": {
"d": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
"requires": {
"es5-ext": "^0.10.9"
@ -6212,7 +6212,7 @@
"dependencies": {
"d": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
"requires": {
"es5-ext": "^0.10.9"
@ -6310,7 +6310,7 @@
"dependencies": {
"d": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
"requires": {
"es5-ext": "^0.10.9"
@ -7081,7 +7081,7 @@
"dependencies": {
"async": {
"version": "1.5.0",
"resolved": "http://registry.npmjs.org/async/-/async-1.5.0.tgz",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.0.tgz",
"integrity": "sha1-J5ZkJyNXOFlWVjP8YnRES+4vjOM="
}
}
@ -8275,13 +8275,13 @@
"dependencies": {
"commander": {
"version": "0.6.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
"resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
"integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
"dev": true
},
"mkdirp": {
"version": "0.3.5",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=",
"dev": true
}
@ -8778,7 +8778,7 @@
},
"load-json-file": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"requires": {
"graceful-fs": "^4.1.2",
@ -8790,7 +8790,7 @@
"dependencies": {
"pify": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
}
}
@ -9063,7 +9063,7 @@
},
"media-typer": {
"version": "0.3.0",
"resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"mem": {
@ -9092,7 +9092,7 @@
"dependencies": {
"next-tick": {
"version": "0.2.2",
"resolved": "http://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz",
"integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0="
}
}
@ -9108,7 +9108,7 @@
},
"meow": {
"version": "3.7.0",
"resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
"resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
"integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
"requires": {
"camelcase-keys": "^2.0.0",
@ -9496,7 +9496,7 @@
"dependencies": {
"readdirp": {
"version": "0.2.5",
"resolved": "http://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz",
"integrity": "sha1-xMJ25Sl3riXbUZH+UdAIVQ8V2bs=",
"dev": true,
"requires": {
@ -9582,7 +9582,7 @@
},
"next-tick": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
},
"ng-dynamic-component": {
@ -9634,7 +9634,7 @@
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
}
}
@ -9670,7 +9670,7 @@
"dependencies": {
"semver": {
"version": "5.3.0",
"resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
},
"tar": {
@ -9768,7 +9768,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
@ -10309,7 +10309,7 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-locale": {
@ -10324,7 +10324,7 @@
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
@ -10558,7 +10558,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
@ -11589,7 +11589,7 @@
},
"pify": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
}
}
@ -11624,7 +11624,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@ -12180,7 +12180,7 @@
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"requires": {
"amdefine": ">=0.0.4"
@ -12257,7 +12257,7 @@
},
"serialize-error": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
"integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go="
},
"serialize-javascript": {
@ -12582,7 +12582,7 @@
},
"socket.io-parser": {
"version": "3.2.0",
"resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
"integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
"dev": true,
"requires": {
@ -13053,7 +13053,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
@ -13061,7 +13061,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@ -13498,7 +13498,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
@ -13641,7 +13641,7 @@
},
"promise": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/promise/-/promise-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz",
"integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=",
"dev": true,
"requires": {
@ -15826,7 +15826,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",

@ -1,20 +1,34 @@
<div *ngIf="modelCount === 0" class="notification-box -info">
<div class="notification-box--content">
<p>
{{ text.empty_warning }}
<br/>
<a *ngIf="canManage"
[href]="ifcData.manageIFCPath"
target="_blank"
[textContent]="text.use_this_link_to_manage">
</a>
</p>
<div #outerContainer
class="ifc-model-viewer">
<div *ngIf="modelCount === 0" class="notification-box -info">
<div class="notification-box--content">
<p>
{{ text.empty_warning }}
<br/>
<a *ngIf="canManage"
[href]="ifcData.manageIFCPath"
target="_blank"
[textContent]="text.use_this_link_to_manage">
</a>
</p>
</div>
</div>
</div>
<div class="ifc-model-viewer--container xeokit-busy-modal-backdrop">
<div class="ifc-model-viewer--toolbar-container"></div>
<canvas class="ifc-model-viewer--model-canvas"></canvas>
</div>
<div class="ifc-model-viewer--container xeokit-busy-modal-backdrop">
<div *ngIf="!keyboardEnabled"
class="ifc-model-viewer--focus-warning">
<a class="ifc-model-viewer--keyboard-disabled-icon icon-no-color icon-input-disabled"
(mousedown)="enableFromIcon($event)"
[attr.title]="text.keyboard_input_disabled">
</a>
</div>
<div class="ifc-model-viewer--toolbar-container"></div>
<canvas class="ifc-model-viewer--model-canvas"
tabindex="0">
</canvas>
</div>
<canvas class="ifc-model-viewer--nav-cube-canvas"></canvas>
<canvas class="ifc-model-viewer--nav-cube-canvas"></canvas>
</div>

@ -0,0 +1,42 @@
@import "helpers"
.ifc-model-viewer
height: 100%
.ifc-model-viewer--nav-cube-canvas
position: absolute
width: 200px
height: 200px
bottom: 0
right: 0
z-index: 10
.ifc-model-viewer--section-planes-overview-canvas
position: absolute
width: 250px
height: 250px
top: 50px
right: 50px
z-index: 2000
.ifc-model-viewer--container
position: relative
width: 100%
height: 100%
padding-bottom: 10px
overflow: hidden
min-width: 420px
.ifc-model-viewer--model-canvas
width: 100%
height: 100%
.ifc-model-viewer--focus-warning
@include without-link-styling
position: absolute
top: 6px
right: 395px
.ifc-model-viewer--keyboard-disabled-icon
font-size: 20px

@ -26,7 +26,15 @@
// See docs/COPYRIGHT.rdoc for more details.
// ++
import {ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
HostListener,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import {IFCViewerService} from "core-app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.service";
import {IfcModelsDataService} from "core-app/modules/bim/ifc_models/pages/viewer/ifc-models-data.service";
import {I18nService} from "core-app/modules/common/i18n/i18n.service";
@ -34,6 +42,7 @@ import {I18nService} from "core-app/modules/common/i18n/i18n.service";
@Component({
selector: 'ifc-viewer',
templateUrl: './ifc-viewer.component.html',
styleUrls: ['./ifc-viewer.component.sass'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IFCViewerComponent implements OnInit, OnDestroy {
@ -45,9 +54,14 @@ export class IFCViewerComponent implements OnInit, OnDestroy {
text = {
empty_warning: this.I18n.t('js.ifc_models.empty_warning'),
use_this_link_to_manage: this.I18n.t('js.ifc_models.use_this_link_to_manage')
use_this_link_to_manage: this.I18n.t('js.ifc_models.use_this_link_to_manage'),
keyboard_input_disabled: this.I18n.t('js.ifc_models.keyboard_input_disabled')
};
keyboardEnabled = false;
@ViewChild('outerContainer') outerContainer:ElementRef;
constructor(private I18n:I18nService,
private elementRef:ElementRef,
private ifcData:IfcModelsDataService,
@ -76,4 +90,27 @@ export class IFCViewerComponent implements OnInit, OnDestroy {
ngOnDestroy():void {
this.ifcViewer.destroy();
}
@HostListener('mousedown')
enableKeyBoard() {
this.keyboardEnabled = true;
this.ifcViewer.setKeyboardEnabled(true);
}
@HostListener('window:mousedown', ['$event.target'])
disableKeyboard(target:Element) {
if (!this.outerContainer.nativeElement!.contains(target)) {
this.keyboardEnabled = false;
this.ifcViewer.setKeyboardEnabled(false);
}
}
enableFromIcon(event:MouseEvent) {
this.enableKeyBoard();
// Ensure we don't bubble this event to the window:mousedown handler
// as the target will already be removed from the DOM by angular
event.stopImmediatePropagation();
return false;
}
}

@ -66,6 +66,10 @@ export class IFCViewerService extends ViewerBridgeService {
this._viewer = viewer;
}
public setKeyboardEnabled(val:boolean) {
this.viewer.setKeyboardEnabled(val);
}
public getViewpoint():Promise<BcfViewpointInterface> {
const viewpoint = this.viewer.saveBCFViewpoint({ spacesVisible: true });

@ -35,35 +35,6 @@
[data-name="bim"] .main-menu--children
overflow-x: hidden !important
.ifc-model-viewer--nav-cube-canvas
position: absolute
width: 200px
height: 200px
bottom: 0
right: 0
z-index: 10
.ifc-model-viewer--section-planes-overview-canvas
position: absolute
width: 250px
height: 250px
top: 50px
right: 50px
z-index: 2000
.ifc-model-viewer--container
position: relative
width: 100%
height: 100%
padding-bottom: 10px
overflow: hidden
min-width: 400px
.ifc-model-viewer--model-canvas
width: 100%
height: 100%
// -------------------------- XEOKIT specific rules for BUTTONS --------------------------
.xeokit-btn-group
vertical-align: middle

@ -12,6 +12,7 @@ en:
ifc_models:
empty_warning: "This project does not yet have any IFC models."
use_this_link_to_manage: "Use this link to upload and manage your IFC models"
keyboard_input_disabled: "Viewer does not have keyboard controls. Click on the viewer to give keyboard control to the viewer."
models:
default: 'Default IFC models'
manage: 'Manage models'

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M21.276 139.317v243.805h472.546V139.317zm62.663 29.162h43.831v43.829H83.94zm61.638 0h43.829v43.829h-43.829zm59.58 0h43.831v43.829h-43.83zm61.638 0h43.829v43.829h-43.829zm58.897 1.37h43.828v43.828h-43.828zm61.635 0h43.831v43.828h-43.83zM105.171 231.77H149v43.83h-43.829zm61.636 0h43.831v43.83h-43.831zm59.582 0h43.829v43.83h-43.829zm61.636 0h43.828v43.83h-43.828zm58.896 1.37h43.831v43.829h-43.83zM83.94 297.228h43.831v43.831H83.94zm61.638 0H363.36v43.831H145.577zm241.75 1.37h43.831v43.831h-43.83z" fill="#1a1a1a"/><path style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" d="M457.579 41.665L22.567 435.963l37.277 35.457 435.012-394.3z" color="#000" font-weight="400" font-family="sans-serif" overflow="visible"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Loading…
Cancel
Save