scrollViewer.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. import { Measure } from "../measure";
  2. import { Rectangle } from "./rectangle";
  3. import { Grid } from "./grid";
  4. import { Control } from "./control";
  5. import { Slider } from "./slider";
  6. import { ValueAndUnit } from "../valueAndUnit";
  7. import { Container } from "./container";
  8. import { TextBlock } from "./textBlock";
  9. import { PointerInfo, Observer, Nullable } from "babylonjs";
  10. /**
  11. * Class used to hold a viewer window and sliders in a grid
  12. */
  13. export class ScrollViewer extends Rectangle {
  14. private _grid: Grid;
  15. private _horizontalBarSpace: Rectangle;
  16. private _verticalBarSpace: Rectangle;
  17. private _dragSpace: Rectangle;
  18. private _horizontalBar: Slider;
  19. private _verticalBar: Slider;
  20. private _barColor: string = "grey";
  21. private _barBorderColor: string = "#444444";
  22. private _barBackground: string = "white";
  23. private _scrollGridWidth: number = 30;
  24. private _scrollGridHeight: number = 30;
  25. private _widthScale: number;
  26. private _heightScale: number;
  27. private _endLeft: number;
  28. private _endTop: number;
  29. private _window: Container;
  30. private _windowContents: Control;
  31. private _pointerIsOver: Boolean = false;
  32. private _wheelPrecision: number = 0.05;
  33. private _onPointerObserver: Nullable<Observer<PointerInfo>>;
  34. /**
  35. * Adds windowContents to the grid view window
  36. * @param windowContents the contents to add the grid view window
  37. */
  38. public addToWindow(windowContents: Control): void {
  39. this._window.removeControl(this._windowContents);
  40. this._windowContents.dispose();
  41. this._windowContents = windowContents;
  42. if (windowContents.typeName === "TextBlock") {
  43. this._updateTextBlock(windowContents);
  44. }
  45. else {
  46. this._updateScroller(windowContents);
  47. }
  48. this._window.addControl(windowContents);
  49. }
  50. /**
  51. * Gets or sets a value indicating the padding to use on the left of the viewer window
  52. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  53. */
  54. public get paddingLeft(): string | number {
  55. return this._windowContents.paddingLeft;
  56. }
  57. /**
  58. * Gets a value indicating the padding in pixels to use on the left of the viewer window
  59. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  60. */
  61. public get paddingLeftInPixels(): number {
  62. return this._windowContents.paddingLeftInPixels;
  63. }
  64. public set paddingLeft(value: string | number) {
  65. this._windowContents.paddingLeft = value;
  66. }
  67. /**
  68. * Gets or sets a value indicating the padding to use on the right of the viewer window
  69. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  70. */
  71. public get paddingRight(): string | number {
  72. return this._windowContents.paddingRight;
  73. }
  74. /**
  75. * Gets a value indicating the padding in pixels to use on the right of the viewer window
  76. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  77. */
  78. public get paddingRightInPixels(): number {
  79. return this._windowContents.paddingRightInPixels;
  80. }
  81. public set paddingRight(value: string | number) {
  82. this._windowContents.paddingRight = value;
  83. }
  84. /**
  85. * Gets or sets a value indicating the padding to use on the top of the viewer window
  86. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  87. */
  88. public get paddingTop(): string | number {
  89. return this._windowContents.paddingTop;
  90. }
  91. /**
  92. * Gets a value indicating the padding in pixels to use on the top of the viewer window
  93. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  94. */
  95. public get paddingTopInPixels(): number {
  96. return this._windowContents.paddingTopInPixels;
  97. }
  98. public set paddingTop(value: string | number) {
  99. this._windowContents.paddingTop = value;
  100. }
  101. /**
  102. * Gets or sets a value indicating the padding to use on the bottom of the viewer window
  103. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  104. */
  105. public get paddingBottom(): string | number {
  106. return this._windowContents.paddingBottom;
  107. }
  108. /**
  109. * Gets a value indicating the padding in pixels to use on the bottom of the viewer window
  110. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  111. */
  112. public get paddingBottomInPixels(): number {
  113. return this._windowContents.paddingBottomInPixels;
  114. }
  115. public set paddingBottom(value: string | number) {
  116. this._windowContents.paddingBottom = value;
  117. }
  118. /**
  119. * Creates a new ScrollViewer
  120. * @param name of ScrollViewer
  121. */
  122. constructor(
  123. /** name of ScrollViewer */
  124. public name?: string) {
  125. super(name);
  126. this.onDirtyObservable.add(() => {
  127. this._horizontalBarSpace.color = this.color;
  128. this._verticalBarSpace.color = this.color;
  129. this._dragSpace.color = this.color;
  130. this._updateScroller(this._windowContents);
  131. if (this._windowContents.typeName === "TextBlock") {
  132. this._updateTextBlock(this._windowContents);
  133. }
  134. });
  135. this.onPointerEnterObservable.add(() => {
  136. this._pointerIsOver = true;
  137. });
  138. this.onPointerOutObservable.add(() => {
  139. this._pointerIsOver = false;
  140. });
  141. this._grid = new Grid();
  142. this._horizontalBar = new Slider();
  143. this._verticalBar = new Slider();
  144. this._window = new Container();
  145. this._window.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  146. this._window.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  147. this._windowContents = new Control();
  148. this._window.addControl(this._windowContents);
  149. this._width = new ValueAndUnit(0.25, ValueAndUnit.UNITMODE_PERCENTAGE, false);
  150. this._height = new ValueAndUnit(0.25, ValueAndUnit.UNITMODE_PERCENTAGE, false);
  151. this._background = "black";
  152. this.fontSize = "16px";
  153. this._grid.addColumnDefinition(1, true);
  154. this._grid.addColumnDefinition(this._scrollGridWidth, true);
  155. this._grid.addRowDefinition(1, true);
  156. this._grid.addRowDefinition(this._scrollGridHeight, true);
  157. this.addControl(this._grid);
  158. this._grid.addControl(this._window, 0, 0);
  159. this._verticalBar.paddingLeft = 0;
  160. this._verticalBar.width = "25px";
  161. this._verticalBar.value = 0;
  162. this._verticalBar.maximum = 100;
  163. this._verticalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  164. this._verticalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  165. this._verticalBar.left = 0.05;
  166. this._verticalBar.isThumbClamped = true;
  167. this._verticalBar.color = "grey";
  168. this._verticalBar.borderColor = "#444444";
  169. this._verticalBar.background = "white";
  170. this._verticalBar.isVertical = true;
  171. this._verticalBar.rotation = Math.PI;
  172. this._verticalBarSpace = new Rectangle();
  173. this._verticalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  174. this._verticalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  175. this._verticalBarSpace.color = this.color;
  176. this._verticalBarSpace.thickness = 1;
  177. this._grid.addControl(this._verticalBarSpace, 0, 1);
  178. this._verticalBarSpace.addControl(this._verticalBar);
  179. this._verticalBar.onValueChangedObservable.add((value) => {
  180. this._window.top = value * this._endTop / 100 + "px";
  181. });
  182. this._horizontalBar.paddingLeft = 0;
  183. this._horizontalBar.height = "25px";
  184. this._horizontalBar.value = 0;
  185. this._horizontalBar.maximum = 100;
  186. this._horizontalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  187. this._horizontalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  188. this._horizontalBar.left = 0.05;
  189. this._horizontalBar.isThumbClamped = true;
  190. this._horizontalBar.color = "grey";
  191. this._horizontalBar.borderColor = "#444444";
  192. this._horizontalBar.background = "white";
  193. this._horizontalBarSpace = new Rectangle();
  194. this._horizontalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  195. this._horizontalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  196. this._horizontalBarSpace.color = this.color;
  197. this._horizontalBarSpace.thickness = 1;
  198. this._grid.addControl(this._horizontalBarSpace, 1, 0);
  199. this._horizontalBarSpace.addControl(this._horizontalBar);
  200. this._horizontalBar.onValueChangedObservable.add((value) => {
  201. this._window.left = value * this._endLeft / 100 + "px";
  202. });
  203. this._dragSpace = new Rectangle();
  204. this._dragSpace.color = this.color;
  205. this._dragSpace.thickness = 2;
  206. this._dragSpace.background = this._barColor;
  207. this._grid.addControl(this._dragSpace, 1, 1);
  208. }
  209. /**
  210. * Gets or sets the mouse wheel precision
  211. * from 0 to 1 with a default value of 0.05
  212. * */
  213. public get wheelPrecision(): number {
  214. return this._wheelPrecision;
  215. }
  216. public set wheelPrecision(value: number) {
  217. if (this._wheelPrecision === value) {
  218. return;
  219. }
  220. if (value < 0) {
  221. value = 0;
  222. }
  223. if (value > 1) {
  224. value = 1;
  225. }
  226. this._wheelPrecision = value;
  227. }
  228. /** Gets or sets the bar color */
  229. public get barColor(): string {
  230. return this._barColor;
  231. }
  232. public set barColor(color: string) {
  233. if (this._barColor === color) {
  234. return;
  235. }
  236. this._barColor = color;
  237. this._horizontalBar.color = color;
  238. this._verticalBar.color = color;
  239. this._dragSpace.background = color;
  240. }
  241. /** Gets or sets the bar color */
  242. public get barBorderColor(): string {
  243. return this._barBorderColor;
  244. }
  245. public set barBorderColor(color: string) {
  246. if (this._barBorderColor === color) {
  247. return;
  248. }
  249. this._barBorderColor = color;
  250. this._horizontalBar.borderColor = color;
  251. this._verticalBar.borderColor = color;
  252. }
  253. /** Gets or sets the bar background */
  254. public get barBackground(): string {
  255. return this._barBackground;
  256. }
  257. public set barBackground(color: string) {
  258. if (this._barBackground === color) {
  259. return;
  260. }
  261. this._barBackground = color;
  262. this._horizontalBar.background = color;
  263. this._verticalBar.background = color;
  264. }
  265. /** @hidden */
  266. protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  267. super._additionalProcessing(parentMeasure, context);
  268. let viewerWidth = this._width.getValueInPixel(this._host, parentMeasure.width);
  269. let viewerHeight = this._height.getValueInPixel(this._host, parentMeasure.height);
  270. let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
  271. let innerHeight = viewerHeight - this._scrollGridHeight - 2 * this.thickness;
  272. this._horizontalBar.width = (innerWidth * 0.8) + "px";
  273. this._verticalBar.height = (innerHeight * 0.8) + "px";
  274. this._grid.setColumnDefinition(0, innerWidth, true);
  275. this._grid.setRowDefinition(0, innerHeight, true);
  276. this._attachWheel();
  277. }
  278. /** @hidden */
  279. private _updateScroller(windowContents: Control): void {
  280. let windowContentsWidth: number = parseFloat(windowContents.width.toString());
  281. if (windowContents._width.unit === 0) {
  282. this._widthScale = windowContentsWidth / 100;
  283. windowContentsWidth = this._host.getSize().width * this._widthScale;
  284. windowContents.width = windowContentsWidth + "px";
  285. }
  286. let windowContentsHeight: number = parseFloat(windowContents.height.toString());
  287. if (windowContents._height.unit === 0) {
  288. this._heightScale = windowContentsHeight / 100;
  289. windowContentsHeight = this._host.getSize().height * this._heightScale;
  290. windowContents.height = this._host.getSize().height * this._heightScale + "px";
  291. }
  292. this._window.width = windowContents.width;
  293. this._window.height = windowContents.height;
  294. this._windowContents.width = windowContents.width;
  295. this._windowContents.height = windowContents.height;
  296. let viewerWidth = this._width.getValueInPixel(this._host, this._host.getSize().width);
  297. let viewerHeight = this._height.getValueInPixel(this._host, this._host.getSize().height);
  298. let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
  299. let innerHeight = viewerHeight - this._scrollGridHeight - 2 * this.thickness;
  300. if (windowContentsWidth <= innerWidth) {
  301. this._grid.setRowDefinition(0, viewerHeight - 2 * this.thickness , true);
  302. this._grid.setRowDefinition(1, 0, true);
  303. this._horizontalBar.isVisible = false;
  304. }
  305. else {
  306. this._grid.setRowDefinition(0, innerHeight, true);
  307. this._grid.setRowDefinition(1, this._scrollGridHeight, true);
  308. this._horizontalBar.isVisible = true;
  309. }
  310. if (windowContentsHeight < innerHeight) {
  311. this._grid.setColumnDefinition(0, viewerWidth - 2 * this.thickness, true);
  312. this._grid.setColumnDefinition(1, 0, true);
  313. this._verticalBar.isVisible = false;
  314. }
  315. else {
  316. this._grid.setColumnDefinition(0, innerWidth, true);
  317. this._grid.setColumnDefinition(1, this._scrollGridWidth, true);
  318. this._verticalBar.isVisible = true;
  319. }
  320. this._endLeft = innerWidth - windowContentsWidth;
  321. this._endTop = innerHeight - windowContentsHeight;
  322. }
  323. /** @hidden */
  324. private _updateTextBlock(windowContents: Control): void {
  325. let viewerWidth = this._width.getValueInPixel(this._host, this._host.getSize().width);
  326. let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
  327. windowContents.width = innerWidth + "px";
  328. this._window.width = windowContents.width;
  329. this._windowContents.width = windowContents.width;
  330. (<TextBlock>windowContents).onLinesReadyObservable.add(() => {
  331. let windowContentsHeight = (this.fontOffset.height) * (<TextBlock>windowContents).lines.length + windowContents.paddingTopInPixels + windowContents.paddingBottomInPixels;
  332. windowContents.height = windowContentsHeight + "px";
  333. this._window.height = windowContents.height;
  334. this._windowContents.height = windowContents.height;
  335. this._updateScroller(windowContents);
  336. });
  337. }
  338. /** @hidden */
  339. private _attachWheel() {
  340. let scene = this._host.getScene();
  341. this._onPointerObserver = scene!.onPointerObservable.add((pi, state) => {
  342. if (!this._pointerIsOver || pi.type !== BABYLON.PointerEventTypes.POINTERWHEEL) {
  343. return;
  344. }
  345. if (this._verticalBar.isVisible == true) {
  346. if ((<MouseWheelEvent>pi.event).deltaY < 0 && this._verticalBar.value > 0) {
  347. this._verticalBar.value -= this._wheelPrecision * 100;
  348. } else if ((<MouseWheelEvent>pi.event).deltaY > 0 && this._verticalBar.value < this._verticalBar.maximum) {
  349. this._verticalBar.value += this._wheelPrecision * 100;
  350. }
  351. }
  352. if (this._horizontalBar.isVisible == true) {
  353. if ((<MouseWheelEvent>pi.event).deltaX < 0 && this._horizontalBar.value < this._horizontalBar.maximum) {
  354. this._horizontalBar.value += this._wheelPrecision * 100;
  355. } else if ((<MouseWheelEvent>pi.event).deltaX > 0 && this._horizontalBar.value > 0) {
  356. this._horizontalBar.value -= this._wheelPrecision * 100;
  357. }
  358. }
  359. });
  360. }
  361. /** Releases associated resources */
  362. public dispose() {
  363. let scene = this._host.getScene();
  364. if (scene && this._onPointerObserver) {
  365. scene.onPointerObservable.remove(this._onPointerObserver);
  366. }
  367. super.dispose();
  368. }
  369. }