🦁

Vue3.js(Tauri vue3 vuetity) から始めるディスクトップアプリ#3 [TautiAPIを使ってみよう!]

2022/11/28に公開

はじめに

Tauriには、前回説明したcommandsを利用した、Rust関数を呼ぶ以外にTautiAPIとして、フロント側よりOS機能を簡単に利用できるようにAPIが用意されています。
APIの種類
app  cli  clipboard  dialog  event fs globalShortcut http mocks
notification os path process shell tauri updater window など
https://tauri.app/v1/api/js/

今回は、windowを操作するAPIと少し毛色の異なるeventを使ってみます。

TauriAPIを利用するための準備

windowAPIを利用するためには、まず利用することを許可しなければならないです。

tauri.conf.json
 "tauri": {
   "allowlist": {
     "window": {
       "all": true, // enable all window APIs
       "create": true, // enable window creation
       "center": true,
       "requestUserAttention": true,
       "setResizable": true,
       "setTitle": true,
       "maximize": true,
       "unmaximize": true,
       "minimize": true,
       "unminimize": true,
       "show": true,
       "hide": true,
       "close": true,
       "setDecorations": true,
       "setAlwaysOnTop": true,
       "setSize": true,
       "setMinSize": true,
       "setMaxSize": true,
       "setPosition": true,
       "setFullscreen": true,
       "setFocus": true,
       "setIcon": true,
       "setSkipTaskbar": true,
       "setCursorGrab": true,
       "setCursorVisible": true,
       "setCursorIcon": true,
       "setCursorPosition": true,
       "setIgnoreCursorEvents": true,
       "startDragging": true,
       "print": true
     }
   }
 }
}

本来は必要に応じてtrueを設定すべきですが、所詮Window操作なので今回は、 "all": true とすべて許可と設定します。

下記のように修正しました。

tauri.conf.json
~
"tauri": {
    "allowlist": {
      "all": false,
+     "window": {
+       "all": true // enable all window APIs
+     }
    },
~

WindowAPIの利用

利用するための準備ができたので、使ってみます。
Window生成時にセットで利用しそうなAPIをピックアップしました。

center(): Promise<void> ウィンドウを中央に配置します。
 maximize(): Promise<void> ウィンドウを最大化する。
 setFocus(): Promise<void> ウィンドウを最前面に移動し、フォーカスを合わせる。

Vue Createdで呼びます。

app.vue
created(){
 console.log("Start!!")
 this.WindowAPI();
}
app.vue
import { appWindow } from "@tauri-apps/api/window";
~
methods: {
  async WindowAPI() {
    await appWindow.center();
    await appWindow.maximize();
    await appWindow.setFocus();
  },
~

promise型なので、awaitで呼びます。画面が最大化で表示されればOKです。

Eventの利用

バックエンドからのイベントを検知します。
イベント種類は、下記の通りです。

Name Type コメント
CHECK_UPDATE "tauri://update"
DOWNLOAD_PROGRESS "tauri://update-download-progress"
INSTALL_UPDATE "tauri://update-install"
MENU "tauri://menu"
STATUS_UPDATE "tauri://update-status"
UPDATE_AVAILABLE "tauri://update-available"
WINDOW_BLUR "tauri://blur"
WINDOW_CLOSE_REQUESTED "tauri://close-requested"
WINDOW_CREATED "tauri://window-created"
WINDOW_DESTROYED "tauri://destroyed"
WINDOW_FILE_DROP "tauri://file-drop"
WINDOW_FILE_DROP_CANCELLED "tauri://file-drop-cancelled"
WINDOW_FILE_DROP_HOVER "tauri://file-drop-hover"
WINDOW_FOCUS "tauri://focus"
WINDOW_MOVED "tauri://move" 左上の座標
WINDOW_RESIZED "tauri://resize" Windowのサイズ
WINDOW_SCALE_FACTOR_CHANGED "tauri://scale-change
WINDOW_THEME_CHANGED "tauri://theme-changed"

今回は、WINDOW_MOVEDとWINDOW_RESIZEDを拾ってみます。

app.vue
created(){
  console.log("Start!!")
  this.WindowAPI();
+ this.Event();
}
```ts:app.vue
import { appWindow } from "@tauri-apps/api/window";
~
export type move = {
  x: number;
  y: number;
};
export type resize = {
  height: number;
  width: number;
};
  methods: {
~
    Event() {
      appWindow.listen("tauri://move", ({ event, payload}) => { 
            let move : move = payload as move;            
            console.log('Potision x=' , move.x , " y=", move.y );
      });
      appWindow.listen("tauri://resize",({ event, payload}) => {
            let resize : resize = payload as resize;  
            console.log('WindowSize x=' ,resize.height , " y=", resize.width );
      });
~

listenでtauri://moveを待ちます。
発火されれば、event に Event名 payloadにデータが入ります。

おわりに

このEventシステムは、electronよりシンプルで直感的で好感が持てます。
複雑なコーティング・省略しすぎるコーティング 自分は、大っ嫌いです。
Simple is Best だれかマイクロソフトに教えてあげてください。 
最後に全コードを載せます。

tauri.conf.json
{
  "$schema": "../node_modules/@tauri-apps/cli/schema.json",
  "build": {
    "beforeBuildCommand": "yarn build",
    "beforeDevCommand": "yarn dev",
    "devPath": "http://localhost:5173",
    "distDir": "../dist"
  },
  "package": {
    "productName": "myproject",
    "version": "0.1.0"
  },
  "tauri": {
    "allowlist": {
      "all": false,
      "window": {
        "all": true 
      }
    },
    "bundle": {
      "active": true,
      "category": "DeveloperTool",
      "copyright": "",
      "deb": {
        "depends": []
      },
      "externalBin": [],
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ],
      "identifier": "com.tauri.dev",
      "longDescription": "",
      "macOS": {
        "entitlements": null,
        "exceptionDomain": "",
        "frameworks": [],
        "providerShortName": null,
        "signingIdentity": null
      },
      "resources": [],
      "shortDescription": "",
      "targets": "all",
      "windows": {
        "certificateThumbprint": null,
        "digestAlgorithm": "sha256",
        "timestampUrl": ""
      }
    },
    "security": {
      "csp": null
    },
    "updater": {
      "active": false
    },
    "windows": [
      {
        "fullscreen": false,
        "height": 600,
        "resizable": true,
        "title": "myproject",
        "width": 800
      }
    ]
  }
}
app.vue
<template>
  <div class="divInput" style="max-width: 400px">
  <label >{{ textMsg }}</label>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { invoke } from '@tauri-apps/api'
import { appWindow } from "@tauri-apps/api/window";
//Dataオブジェクト
export type DataType = {
  text: string;
};
export type move = {
  x: number;
  y: number;
};
export type resize = {
  height: number;
  width: number;
};
export default defineComponent({
  name: 'App',
  components: {},
  data(): DataType {
    return {
      //nameの初期化
      text: '',
    };
  },
  computed: {
    textMsg(){        
      invoke('greet',{name: "World"})
        .then((response) => { if (typeof response === 'string'){
                                this.text = response;
                              }else{
                                console.log(response)
                                this.text =  'Error';
                              }
        })
        return this.text;
        },
  },
  methods: {
    async WindowAPI() {
      await appWindow.center();
      await appWindow.maximize();
      await appWindow.setFocus();
    },
    Event() {
      appWindow.listen("tauri://move", ({ event, payload}) => { 
            let move : move = payload as move;            
            console.log('Potision x=' , move.x , " y=", move.y );
      });
      appWindow.listen("tauri://resize",({ event, payload}) => {
            let resize : resize = payload as resize;  
            console.log('WindowSize x=' ,resize.height , " y=", resize.width );
      });
    }   
  },
  watch: {},
  created(){
    console.log("Start!!")
    this.WindowAPI();
    this.Event();
  }
});

</script>
<style scoped>
</style>

Discussion