Window.svelte 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <script>
  2. import {
  3. appWindow,
  4. WebviewWindow,
  5. LogicalSize,
  6. UserAttentionType,
  7. PhysicalSize,
  8. PhysicalPosition
  9. } from '@tauri-apps/api/window'
  10. let selectedWindow = appWindow.label
  11. const windowMap = {
  12. [appWindow.label]: appWindow
  13. }
  14. const cursorIconOptions = [
  15. 'default',
  16. 'crosshair',
  17. 'hand',
  18. 'arrow',
  19. 'move',
  20. 'text',
  21. 'wait',
  22. 'help',
  23. 'progress',
  24. // something cannot be done
  25. 'notAllowed',
  26. 'contextMenu',
  27. 'cell',
  28. 'verticalText',
  29. 'alias',
  30. 'copy',
  31. 'noDrop',
  32. // something can be grabbed
  33. 'grab',
  34. /// something is grabbed
  35. 'grabbing',
  36. 'allScroll',
  37. 'zoomIn',
  38. 'zoomOut',
  39. // edge is to be moved
  40. 'eResize',
  41. 'nResize',
  42. 'neResize',
  43. 'nwResize',
  44. 'sResize',
  45. 'seResize',
  46. 'swResize',
  47. 'wResize',
  48. 'ewResize',
  49. 'nsResize',
  50. 'neswResize',
  51. 'nwseResize',
  52. 'colResize',
  53. 'rowResize'
  54. ]
  55. export let onMessage
  56. let newWindowLabel
  57. let urlValue = 'https://tauri.app'
  58. let resizable = true
  59. let maximized = false
  60. let decorations = true
  61. let alwaysOnTop = false
  62. let contentProtected = true
  63. let fullscreen = false
  64. let width = null
  65. let height = null
  66. let minWidth = null
  67. let minHeight = null
  68. let maxWidth = null
  69. let maxHeight = null
  70. let x = null
  71. let y = null
  72. let scaleFactor = 1
  73. let innerPosition = new PhysicalPosition(x, y)
  74. let outerPosition = new PhysicalPosition(x, y)
  75. let innerSize = new PhysicalSize(width, height)
  76. let outerSize = new PhysicalSize(width, height)
  77. let resizeEventUnlisten
  78. let moveEventUnlisten
  79. let cursorGrab = false
  80. let cursorVisible = true
  81. let cursorX = null
  82. let cursorY = null
  83. let cursorIcon = 'default'
  84. let cursorIgnoreEvents = false
  85. let windowTitle = 'Awesome Tauri Example!'
  86. function setTitle_() {
  87. windowMap[selectedWindow].setTitle(windowTitle)
  88. }
  89. function hide_() {
  90. windowMap[selectedWindow].hide()
  91. setTimeout(windowMap[selectedWindow].show, 2000)
  92. }
  93. function minimize_() {
  94. windowMap[selectedWindow].minimize()
  95. setTimeout(windowMap[selectedWindow].unminimize, 2000)
  96. }
  97. function createWindow() {
  98. if (!newWindowLabel) return
  99. const webview = new WebviewWindow(newWindowLabel)
  100. windowMap[newWindowLabel] = webview
  101. webview.once('tauri://error', function () {
  102. onMessage('Error creating new webview')
  103. })
  104. }
  105. function loadWindowSize() {
  106. windowMap[selectedWindow].innerSize().then((response) => {
  107. innerSize = response
  108. width = innerSize.width
  109. height = innerSize.height
  110. })
  111. windowMap[selectedWindow].outerSize().then((response) => {
  112. outerSize = response
  113. })
  114. }
  115. function loadWindowPosition() {
  116. windowMap[selectedWindow].innerPosition().then((response) => {
  117. innerPosition = response
  118. })
  119. windowMap[selectedWindow].outerPosition().then((response) => {
  120. outerPosition = response
  121. x = outerPosition.x
  122. y = outerPosition.y
  123. })
  124. }
  125. async function addWindowEventListeners(window) {
  126. if (!window) return
  127. if (resizeEventUnlisten) {
  128. resizeEventUnlisten()
  129. }
  130. if (moveEventUnlisten) {
  131. moveEventUnlisten()
  132. }
  133. moveEventUnlisten = await window.listen('tauri://move', loadWindowPosition)
  134. resizeEventUnlisten = await window.listen('tauri://resize', loadWindowSize)
  135. }
  136. async function requestUserAttention_() {
  137. await windowMap[selectedWindow].minimize()
  138. await windowMap[selectedWindow].requestUserAttention(
  139. UserAttentionType.Critical
  140. )
  141. await new Promise((resolve) => setTimeout(resolve, 3000))
  142. await windowMap[selectedWindow].requestUserAttention(null)
  143. }
  144. $: {
  145. windowMap[selectedWindow]
  146. loadWindowPosition()
  147. loadWindowSize()
  148. }
  149. $: windowMap[selectedWindow]?.setResizable(resizable)
  150. $: maximized
  151. ? windowMap[selectedWindow]?.maximize()
  152. : windowMap[selectedWindow]?.unmaximize()
  153. $: windowMap[selectedWindow]?.setDecorations(decorations)
  154. $: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop)
  155. $: windowMap[selectedWindow]?.setContentProtected(contentProtected)
  156. $: windowMap[selectedWindow]?.setFullscreen(fullscreen)
  157. $: width &&
  158. height &&
  159. windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height))
  160. $: minWidth && minHeight
  161. ? windowMap[selectedWindow]?.setMinSize(
  162. new LogicalSize(minWidth, minHeight)
  163. )
  164. : windowMap[selectedWindow]?.setMinSize(null)
  165. $: maxWidth > 800 && maxHeight > 400
  166. ? windowMap[selectedWindow]?.setMaxSize(
  167. new LogicalSize(maxWidth, maxHeight)
  168. )
  169. : windowMap[selectedWindow]?.setMaxSize(null)
  170. $: x !== null &&
  171. y !== null &&
  172. windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y))
  173. $: windowMap[selectedWindow]
  174. ?.scaleFactor()
  175. .then((factor) => (scaleFactor = factor))
  176. $: addWindowEventListeners(windowMap[selectedWindow])
  177. $: windowMap[selectedWindow]?.setCursorGrab(cursorGrab)
  178. $: windowMap[selectedWindow]?.setCursorVisible(cursorVisible)
  179. $: windowMap[selectedWindow]?.setCursorIcon(cursorIcon)
  180. $: cursorX !== null &&
  181. cursorY !== null &&
  182. windowMap[selectedWindow]?.setCursorPosition(
  183. new PhysicalPosition(cursorX, cursorY)
  184. )
  185. $: windowMap[selectedWindow]?.setIgnoreCursorEvents(cursorIgnoreEvents)
  186. </script>
  187. <div class="flex flex-col children:grow gap-2">
  188. <div class="flex gap-1">
  189. <input
  190. class="input grow"
  191. type="text"
  192. placeholder="New Window label.."
  193. bind:value={newWindowLabel}
  194. />
  195. <button class="btn" on:click={createWindow}>New window</button>
  196. </div>
  197. <br />
  198. {#if Object.keys(windowMap).length >= 1}
  199. <span class="font-700 text-sm">Selected window:</span>
  200. <select class="input" bind:value={selectedWindow}>
  201. <option value="" disabled selected>Choose a window...</option>
  202. {#each Object.keys(windowMap) as label}
  203. <option value={label}>{label}</option>
  204. {/each}
  205. </select>
  206. {/if}
  207. {#if windowMap[selectedWindow]}
  208. <br />
  209. <div class="flex flex-wrap gap-2">
  210. <button
  211. class="btn"
  212. title="Unminimizes after 2 seconds"
  213. on:click={() => windowMap[selectedWindow].center()}
  214. >
  215. Center
  216. </button>
  217. <button
  218. class="btn"
  219. title="Unminimizes after 2 seconds"
  220. on:click={minimize_}
  221. >
  222. Minimize
  223. </button>
  224. <button
  225. class="btn"
  226. title="Visible again after 2 seconds"
  227. on:click={hide_}
  228. >
  229. Hide
  230. </button>
  231. <button
  232. class="btn"
  233. on:click={requestUserAttention_}
  234. title="Minimizes the window, requests attention for 3s and then resets it"
  235. >Request attention</button
  236. >
  237. </div>
  238. <br />
  239. <div class="flex flex-wrap gap-2">
  240. <label>
  241. Maximized
  242. <input type="checkbox" bind:checked={maximized} />
  243. </label>
  244. <label>
  245. Resizable
  246. <input type="checkbox" bind:checked={resizable} />
  247. </label>
  248. <label>
  249. Has decorations
  250. <input type="checkbox" bind:checked={decorations} />
  251. </label>
  252. <label>
  253. Always on top
  254. <input type="checkbox" bind:checked={alwaysOnTop} />
  255. </label>
  256. <label>
  257. Content protected
  258. <input type="checkbox" bind:checked={contentProtected} />
  259. </label>
  260. <label>
  261. Fullscreen
  262. <input type="checkbox" bind:checked={fullscreen} />
  263. </label>
  264. </div>
  265. <br />
  266. <div class="flex flex-row gap-2 flex-wrap">
  267. <div class="flex children:grow flex-col">
  268. <div>
  269. X
  270. <input class="input" type="number" bind:value={x} min="0" />
  271. </div>
  272. <div>
  273. Y
  274. <input class="input" type="number" bind:value={y} min="0" />
  275. </div>
  276. </div>
  277. <div class="flex children:grow flex-col">
  278. <div>
  279. Width
  280. <input class="input" type="number" bind:value={width} min="400" />
  281. </div>
  282. <div>
  283. Height
  284. <input class="input" type="number" bind:value={height} min="400" />
  285. </div>
  286. </div>
  287. <div class="flex children:grow flex-col">
  288. <div>
  289. Min width
  290. <input class="input" type="number" bind:value={minWidth} />
  291. </div>
  292. <div>
  293. Min height
  294. <input class="input" type="number" bind:value={minHeight} />
  295. </div>
  296. </div>
  297. <div class="flex children:grow flex-col">
  298. <div>
  299. Max width
  300. <input class="input" type="number" bind:value={maxWidth} min="800" />
  301. </div>
  302. <div>
  303. Max height
  304. <input class="input" type="number" bind:value={maxHeight} min="400" />
  305. </div>
  306. </div>
  307. </div>
  308. <br />
  309. <div>
  310. <div class="flex">
  311. <div class="grow">
  312. <div class="text-accent dark:text-darkAccent font-700">
  313. Inner Size
  314. </div>
  315. <span>Width: {innerSize.width}</span>
  316. <span>Height: {innerSize.height}</span>
  317. </div>
  318. <div class="grow">
  319. <div class="text-accent dark:text-darkAccent font-700">
  320. Outer Size
  321. </div>
  322. <span>Width: {outerSize.width}</span>
  323. <span>Height: {outerSize.height}</span>
  324. </div>
  325. </div>
  326. <div class="flex">
  327. <div class="grow">
  328. <div class="text-accent dark:text-darkAccent font-700">
  329. Inner Logical Size
  330. </div>
  331. <span>Width: {innerSize.toLogical(scaleFactor).width}</span>
  332. <span>Height: {innerSize.toLogical(scaleFactor).height}</span>
  333. </div>
  334. <div class="grow">
  335. <div class="text-accent dark:text-darkAccent font-700">
  336. Outer Logical Size
  337. </div>
  338. <span>Width: {outerSize.toLogical(scaleFactor).width}</span>
  339. <span>Height: {outerSize.toLogical(scaleFactor).height}</span>
  340. </div>
  341. </div>
  342. <div class="flex">
  343. <div class="grow">
  344. <div class="text-accent dark:text-darkAccent font-700">
  345. Inner Position
  346. </div>
  347. <span>x: {innerPosition.x}</span>
  348. <span>y: {innerPosition.y}</span>
  349. </div>
  350. <div class="grow">
  351. <div class="text-accent dark:text-darkAccent font-700">
  352. Outer Position
  353. </div>
  354. <span>x: {outerPosition.x}</span>
  355. <span>y: {outerPosition.y}</span>
  356. </div>
  357. </div>
  358. <div class="flex">
  359. <div class="grow">
  360. <div class="text-accent dark:text-darkAccent font-700">
  361. Inner Logical Position
  362. </div>
  363. <span>x: {innerPosition.toLogical(scaleFactor).x}</span>
  364. <span>y: {innerPosition.toLogical(scaleFactor).y}</span>
  365. </div>
  366. <div class="grow">
  367. <div class="text-accent dark:text-darkAccent font-700">
  368. Outer Logical Position
  369. </div>
  370. <span>x: {outerPosition.toLogical(scaleFactor).x}</span>
  371. <span>y: {outerPosition.toLogical(scaleFactor).y}</span>
  372. </div>
  373. </div>
  374. </div>
  375. <br />
  376. <h4 class="mb-2">Cursor</h4>
  377. <div class="flex gap-2">
  378. <label>
  379. <input type="checkbox" bind:checked={cursorGrab} />
  380. Grab
  381. </label>
  382. <label>
  383. <input type="checkbox" bind:checked={cursorVisible} />
  384. Visible
  385. </label>
  386. <label>
  387. <input type="checkbox" bind:checked={cursorIgnoreEvents} />
  388. Ignore events
  389. </label>
  390. </div>
  391. <div class="flex gap-2">
  392. <label>
  393. Icon
  394. <select class="input" bind:value={cursorIcon}>
  395. {#each cursorIconOptions as kind}
  396. <option value={kind}>{kind}</option>
  397. {/each}
  398. </select>
  399. </label>
  400. <label>
  401. X position
  402. <input class="input" type="number" bind:value={cursorX} />
  403. </label>
  404. <label>
  405. Y position
  406. <input class="input" type="number" bind:value={cursorY} />
  407. </label>
  408. </div>
  409. <br />
  410. <div class="flex flex-col gap-1">
  411. <form class="flex gap-1" on:submit|preventDefault={setTitle_}>
  412. <input class="input grow" id="title" bind:value={windowTitle} />
  413. <button class="btn" type="submit">Set title</button>
  414. </form>
  415. </div>
  416. {/if}
  417. </div>