wxStyledTextCtrlはscintillaというオープンソースのエディタを元に実装されたコントロールで、様々な言語をハイライトできる。
例えば以下のようにStyleSetForegroundを使用すると設定した項目をハイライトできる
//wxStyledTextCtrl #include <wx/stc/stc.h> /* ... */ // ウィンドウ作成 class MyFrame : public wxFrame { public: void PostCreate() {
auto editor = new wxStyledTextCtrl(this, wxID_ANY); editor->SetLexer(wxSTC_LEX_HTML); // HTMLのシンタックスハイライトを設定 editor->StyleSetForeground(wxSTC_H_TAG, wxColour(0, 0, 255)); // タグの色を青に設定 editor->StyleSetForeground(wxSTC_H_ATTRIBUTE, wxColour(255, 255, 0)); editor->StyleSetForeground(wxSTC_H_VALUE, wxColour(0, 0, 0)); editor->StyleSetForeground(wxSTC_H_COMMENT, wxColour(0, 255, 0));
this->Layout(); // レイアウトの更新
}
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
{
CallAfter(&MyFrame::PostCreate);
}
private:
};
/* ... */
void PostCreate() { // #include <wx/stc/stc.h> が必要 auto editor = new wxStyledTextCtrl(this, wxID_ANY); // テキストの設定 editor->SetText("<html>\n<head>\n<title>Sample</title>\n</head>\n<body>\n<!-- 本文 -->\n<h1>Hello World</h1>\n</body>\n</html>"); // テキストの設定 editor->SetLexer(wxSTC_LEX_HTML); // HTMLのシンタックスハイライトを設定 editor->StyleSetForeground(wxSTC_H_TAG, wxColour(0, 0, 255)); // タグの色を赤に設定 editor->StyleSetForeground(wxSTC_H_ATTRIBUTE, wxColour(255, 0, 255)); editor->StyleSetForeground(wxSTC_H_VALUE, wxColour(0, 0, 0)); editor->StyleSetForeground(wxSTC_H_COMMENT, wxColour(0, 255, 0)); // コメントの色を緑に設定 editor->StyleSetBackground(wxSTC_H_COMMENT, wxColour(0, 0, 0)); // コメントの背景色を黒に設定 this->Layout(); // レイアウトの更新 }
void PostCreate() { auto editor = new wxStyledTextCtrl(this, wxID_ANY); editor->SetLexer(wxSTC_LEX_HTML); // HTMLのシンタックスハイライトを設定 //// 左側にマージンを追加し、その列に表示するものを設定 //// マージンインデクスは左側に追加する列番号と考えればよい //// マージンに表示するものを行番号に設定 editor->SetMarginType(0, wxSTC_MARGIN_NUMBER);
//// 左側にマージン(スペース)を作成 editor->SetMarginWidth(0/*マージンインデクス*/, 40/*マージンのピクセル幅*/); // テキストの設定 editor->SetText(R"( <html> <body> <div> <!-- 本文 --> <h1>Hello World</h1> </div> </body> </html>)"); /* ... */
this->Layout(); // レイアウトの更新 }

void PostCreate() { auto editor = new wxStyledTextCtrl(this, wxID_ANY); editor->SetLexer(wxSTC_LEX_HTML); // HTMLのシンタックスハイライトを設定 // インデントガイド editor->SetIndentationGuides(wxSTC_IV_LOOKBOTH); //editor->SetTabWidth(4); editor->SetIndent(4); editor->StyleSetForeground(wxSTC_STYLE_INDENTGUIDE, wxColour(50, 50, 50)); // インデントガイドの色を設定 // テキストの設定 editor->SetText(R"( <html> <body> <div> <!-- 本文 --> <h1>Hello World</h1> </div> </body> </html>)"); // テキストの設定 /* ... */
this->Layout(); // レイアウトの更新 }
void PostCreate() { editor = new wxStyledTextCtrl(this, wxID_ANY); //editor->SetLexer(wxSTC_LEX_HTML); // HTMLのシンタックスハイライトを設定 editor->SetLexer(wxSTC_LEX_CPP); // マージン1をコード折り畳み用のシンボルマージンとして設定 folder_margin_id = 1; editor->SetMarginType(folder_margin_id, wxSTC_MARGIN_SYMBOL); editor->SetMarginMask(folder_margin_id, wxSTC_MASK_FOLDERS); //editor->SetMarginWidth(1, 16); // マージンの幅を設定 editor->SetMarginSensitive(folder_margin_id, true); // マージンをクリック可能にする // 折り畳みのマーカーの設定 // editor->MarkerDefine(設定する対象 , どのマークを使うか , 背景色, 前景色) // トップレベルの+/-マーカーを設定 editor->MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS, wxColour(255, 255, 255), wxColour(0, 0, 0)); editor->MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS, wxColour(255, 255, 255), wxColour(0, 0, 0)); // 垂直線を引く editor->MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE,wxColour(0,0,0), wxColour(0, 0, 0)); // 折り畳みの中間の+/-マーカーを設定 editor->MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED, wxColour(255, 0, 0), wxColour(0, 0, 0)); editor->MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED, wxColour(0,255, 0), wxColour(255, 255, 255)); // 折り畳みの中間の終了マーク editor->MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_TCORNER, wxColour(0, 0, 0), wxColour(255, 0, 255)); // トップレベルの終了マーク editor->MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER, wxColour(0, 0, 0), wxColour(0, 255, 255)); // 折り畳みの有効化 editor->SetProperty("fold", "1"); editor->SetFoldFlags(0 /*wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_EXPANDED*/); // テキストの設定 editor->SetText(R"(
#include <iostream> int main() { int i = 0; for(i = 0; i < 10; i++) { if( i % 2 == 0) { std::cout << "even "; } else { std::cout << "odd "; } std::cout << i << std::endl; } return 0; }
)"); editor->Bind(wxEVT_STC_MARGINCLICK, &MyFrame::OnMarginClick, this); this->Layout(); // レイアウトの更新 }
void OnMarginClick(wxStyledTextEvent& event) { // マージンに表示されたマークをクリックしたときのイベント処理 if (event.GetMargin() == folder_margin_id) { int lineClick = editor->LineFromPosition(event.GetPosition()); int levelClick = editor->GetFoldLevel(lineClick); if ((levelClick & wxSTC_FOLDLEVELHEADERFLAG) > 0) { editor->ToggleFold(lineClick); } } }
公式に詳しく書いてある。
{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} ... C++であることを表すGUID。定数。
TestApplicationCPP ... プロジェクト名。ただし変えても影響ない(なぜ)。
TestApplicationCPP\TestApplicationCPP.vcxproj ... プロジェクトファイルへのパス
{BCFFEF98-34EF-437F-BE38-8A084328984F} ... プロジェクトを一意に表す識別子。GUID。プロジェクトを作るたびに変わる。
{722044A8-0732-4C11-87C7-85A5DDFF9F78} ... ソリューションを一意に表す識別子。GUID。ソリューションを作るたびに変わる。
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestApplicationCPP", "TestApplicationCPP\TestApplicationCPP.vcxproj", "{BCFFEF98-34EF-437F-BE38-8A084328984F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Debug|x64.ActiveCfg = Debug|x64
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Debug|x64.Build.0 = Debug|x64
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Debug|x86.ActiveCfg = Debug|Win32
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Debug|x86.Build.0 = Debug|Win32
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Release|x64.ActiveCfg = Release|x64
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Release|x64.Build.0 = Release|x64
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Release|x86.ActiveCfg = Release|Win32
{BCFFEF98-34EF-437F-BE38-8A084328984F}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {722044A8-0732-4C11-87C7-85A5DDFF9F78}
EndGlobalSection
EndGlobal
TestApplicationCPPの部分は変更してもどこにも反映されず影響もないが、直後のvcxprojへのパスが間違った場合のみ、エラー表記中にこの文字列が表示される。
せっかくなので.vcxprojファイルの中も見てみる。こちらはxmlファイル。
例としてLuaのinclude設定、ライブラリ設定をしたプロジェクトを作成した。
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration>
<ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals"> <VCProjectVersion>16.0</VCProjectVersion> <Keyword>Win32Proj</Keyword> <ProjectGuid>{bcffef98-34ef-437f-be38-8a084328984f}</ProjectGuid> <RootNamespace>TestApplicationCPP</RootNamespace> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> <ProjectName>TestApplicationCPP</ProjectName> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> <PlatformToolset>v143</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> <PlatformToolset>v143</PlatformToolset> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> <PlatformToolset>v143</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> <PlatformToolset>v143</PlatformToolset> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Label="Shared"> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <WarningLevel>Level3</WarningLevel> <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> </ClCompile>
<Link> <SubSystem>Console</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> </Link> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <WarningLevel>Level3</WarningLevel> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> </ClCompile>
<Link> <SubSystem>Console</SubSystem> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> </Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <WarningLevel>Level3</WarningLevel> <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <AdditionalIncludeDirectories>C:\libraries\lua\include</AdditionalIncludeDirectories> </ClCompile>
<Link> <SubSystem>Console</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalDependencies>lua54.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>C:\libraries\lua</AdditionalLibraryDirectories> </Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <WarningLevel>Level3</WarningLevel> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <AdditionalIncludeDirectories>C:\libraries\lua\include</AdditionalIncludeDirectories> </ClCompile>
<Link> <SubSystem>Console</SubSystem> <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalLibraryDirectories>C:\libraries\lua</AdditionalLibraryDirectories> <AdditionalDependencies>lua54.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link>
</ItemDefinitionGroup>
<ItemGroup> <ClCompile Include="TestApplicationCPP.cpp" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup>
</Project>
Voxel Pluginを使ってみる。なお著者はUnreal Engineのプラグインを使うのが初めてである。
以下からダウンロード・インストール。なんかクリックしていくといつの間にか入っている。
https://www.unrealengine.com/marketplace/ja/product/voxel-plugin-free

どれでもいいが今回は地形をVoxelWorldで作成するので、「ファーストパーソン」を選択し、床を削除する。

次にプラグインを有効にする。[編集][プラグイン]でプラグイン画面を開き、検索にvoxelと打つと補完で出てくるのでチェックする。
有効化のためには再起動が必要。


[Voxel] → [VoxelWorld]を選択するとボクセルの地形が追加される。
少し上のほうに出てくるかもしれないので位置は自分のスポーン位置より低いところに移動する


Static Cylinderを追加して、自分のスポーン位置の目の前、少し上のあたりに配置。
まずブループリントクラスに変換する。

VoxelWorldの設定からOn Component Hitイベントを追加する。

Cylinderが落下して発生するOn Component Hitでは、ぶつかったものがCylinderで、ぶつけられたほうがVoxelWorldになる。
On Component Hit が発生したら、ぶつけられたほうをVoxelWorld型に変更し、ボクセルを消す処理をを呼び出す。

後で重力の設定をして落下とOn Component Hitの処理を有効化する。

GUIDは全世界でユニークなID。
UUID(Universally Unique Identifier)は、ソフトウェア上でオブジェクトを一意に識別するための識別子である。
https://ja.wikipedia.org/wiki/UUID
https://ja.wikipedia.org/wiki/UUID
GUID (英: Globally Unique Identifier) またはグローバル一意識別子(ぐろーばるいちいしきべつし)は、UUIDの実装のひとつ、あるいは(事実上)UUIDの別名である。
https://ja.wikipedia.org/wiki/GUID
#include <iostream> // CoCreateGuidを使用するためのヘッダファイル #include <combaseapi.h> //※ #include <Windows.h>をインクルードすれば使える int main() { GUID guid; CoCreateGuid(&guid); // GUIDを生成 wchar_t* guidString; StringFromCLSID(guid, &guidString); // GUIDを文字列に変換 // GUIDを表示 std::wcout << guidString << std::endl; // メモリ解放 CoTaskMemFree(guidString); }
記事があまりにさみしいので.NET版を置いておく。Guid.NewGuid()でGUIDを取得できる。
using System; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { System.Guid guid = Guid.NewGuid(); Console.WriteLine("D " + guid.ToString("D")); Console.WriteLine("N " + guid.ToString("N")); Console.WriteLine("B " + guid.ToString("B")); Console.WriteLine("P " + guid.ToString("P")); Console.ReadKey(); } } }
Boost.Scopeにも実装されているらしい。
https://www.boost.org/doc/libs/1_85_0/libs/scope/doc/html/index.html
ただBoostの導入は面倒なので、今回は有志がGithubで公開しているものを使う。
https://github.com/okdshin/unique_resource
例えば、C++にはstd::unique_ptrがあり、メモリリークを防げる。この時、カスタムデリータを指定すれば、普通のnew/deleteとは異なった破棄処理を行うこともできる。
// ファイルをクローズするデリータ struct MyFileCloser { void operator()(FILE* ptr) const { fclose(ptr); } };
int main() { { // ファイルを開く std::unique_ptr<FILE, MyFileCloser> pfile(fopen("test", "r"), MyFileCloser()); char buffer[1024]; fgets(buffer, sizeof(buffer), pfile.get()); std::cout << buffer << std::endl; // ファイルはスコープを抜けると自動的にクローズされる } }
問題は管理したい参照がポインタでない場合で、unique_ptrではエラーになる。
#include <memory> #include <unordered_map> // リソースはハンドルでアクセス using HANDLE = int; // リソース管理用のコンテナ std::unordered_map<HANDLE, int*> myresource;
// リソースの確保 HANDLE MyNew(size_t size) { HANDLE handle = 0; myresource.insert({ handle,new int[size] }); return handle++; }
// リソースの解放 void MyDelete(HANDLE handle) { delete[] myresource[handle]; myresource.erase(handle); }
struct My_HANDLE_Release { void operator()(HANDLE handle) const { MyDelete(handle); } };
int main() { // エラー MyNew が返す HANDLE はポインタでない std::unique_ptr<HANDLE, My_HANDLE_Release> uptr(MyNew(100), My_HANDLE_Release()); }
以下から、unique_resource.hpp をダウンロード。
https://github.com/okdshin/unique_resource
#include "unique_resource.hpp"
int main() { HANDLE test; { auto unique_handle = std_experimental::make_unique_resource<HANDLE, My_HANDLE_Release>(MyNew(100), My_HANDLE_Release()); test = unique_handle.get(); // リソースを使う int* p = myresource.at(unique_handle.get()); std::cout << "access " << p << std::endl; for (size_t i = 0; i < 100; ++i) { p[i] = i; }
// リソースが解放される
}
// リソースが解放されているかチェック if( myresource.find(test) == myresource.end() ) { std::cout << "リソースは解放されています" << std::endl; } else { std::cout << "リソースは解放されていません" << std::endl; } }
window.addEventListener("DOMContentLoaded", () => { document.getElementById("MyNewButton").addEventListener("click",async (e) => { invoke("my_rust_messagebox"); }); });
Rust側では、tauriクレートのfeaturesでdialogを指定する。
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[tauri::command] fn my_rust_messagebox(window:tauri::Window){ tauri::api::dialog::message(Some(&window),"タイトル","メッセージ "); }
fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![my_rust_messagebox]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
以下の3ファイルが、GUIの編集とイベント処理に必要になる
index.htmlに、buttonを追加。IDをMyNewButtonとする。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="styles.css" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Tauri App</title> <script type="module" src="/main.js" defer></script> <style> .logo.vanilla:hover { filter: drop-shadow(0 0 2em #ffe21c); } </style> </head> <body> <div class="container"> <h1>Welcome to Tauri!</h1> <div class="row"> <a href="https://tauri.app" target="_blank"> <img src="/assets/tauri.svg" class="logo tauri" alt="Tauri logo" /> </a> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" > <img src="/assets/javascript.svg" class="logo vanilla" alt="JavaScript logo" /> </a> </div> <p>Click on the Tauri logo to learn more about the framework</p> <form class="row" id="greet-form"> <input id="greet-input" placeholder="Enter a name..." /> <button type="submit">Greet</button> </form> <p id="greet-msg"></p> <p></p> <button id="MyNewButton" type="button">新しいボタン</button> </div> </body> </html>
Rust側の関数 my_rust_function を呼び出すJavascriptを記述。myvalueは引数。
const { invoke } = window.__TAURI__.tauri; let greetInputEl; let greetMsgEl; async function greet() { // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command greetMsgEl.textContent = await invoke("greet", { name: greetInputEl.value }); } window.addEventListener("DOMContentLoaded", () => { greetInputEl = document.querySelector("#greet-input"); greetMsgEl = document.querySelector("#greet-msg"); document.querySelector("#greet-form").addEventListener("submit", (e) => { e.preventDefault(); greet(); });
document.getElementById("MyNewButton").addEventListener("click",async (e) => { invoke("my_rust_function", { myvalue: 55555 }); });
});
// Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) }
#[tauri::command] fn my_rust_function(myvalue: i32){ // カレントディレクトリへテキストファイルを作成 std::fs::write("my_rust_function.txt", format!("myvalue: {}", myvalue)).unwrap(); }
fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet]) .invoke_handler(tauri::generate_handler![my_rust_function]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
my_rust_function.txtが生成される。
RustでGUIを扱うよい方法を探している。
Node.jsは、クライアントPCで動くJavaScript実行環境。Node.jsの公式サイトからダウンロードする。LTS版とCurrent版があるがそれぞれ安全版と最新版程度の違いでしかないので好きなほうでいい。
例のごとく、私はmsiによるインストールを好まないので、Other Downloadsからzip版をダウンロードして導入して自分でパスを通した。
Tauri Command Line Interface。Tauri開発に必要なコマンドラインツール。
Rust環境は構築済みであることが前提なので、以下のコマンドでtauri-cliを導入する。
最初に、プロジェクトを生成するディレクトリへCDする。
以下のコマンドを叩くと、対話形式でのtauriプロジェクトの生成が始まる。
Project name プロジェクト名を入力 例:my1st_tauri
Choose which language to use for your frontend カーソル上下でRustを選択してEnter
Choose your UI template 今回はVanillaを選択。選択肢によってプロジェクト構成が違う。
まず作成したプロジェクトのディレクトリへ入る。
続いて、プロジェクトをビルド・実行
デバッグビルド
リリースビルド
ただし、これを実行すると、以下のラーが出る
このエラーはBundle.identifierという設定項目が初期値のままの時に発生する。
バンドルIDは、アプリケーション固有のIDで、何でもいいが他のTauriアプリケーションと重複しないIDを与える。一般的にはドメインを逆にしたものを与えるらしい。
src-tauri\tauri.conf.jsonを以下のように変更し、再度cargo tauri buildする。
Before
{ "build": { "devPath": "../src", "distDir": "../src", "withGlobalTauri": true }, "package": { "productName": "tauri-app", "version": "0.0.0" }, "tauri": { "allowlist": { "all": false, "shell": { "all": false, "open": true } }, "windows": [ { "title": "tauri-app", "width": 800, "height": 600 } ], "security": { "csp": null }, "bundle": { "active": true, "targets": "all", "identifier": "com.tauri.dev", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ] } } }
After
{ "build": { "devPath": "../src", "distDir": "../src", "withGlobalTauri": true }, "package": { "productName": "tauri-app", "version": "0.0.0" }, "tauri": { "allowlist": { "all": false, "shell": { "all": false, "open": true } }, "windows": [ { "title": "tauri-app", "width": 800, "height": 600 } ], "security": { "csp": null }, "bundle": { "active": true, "targets": "all", "identifier": "my.unique.identify", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ] } } }
なお、cargo tauri devでデバッグビルドと同時に実行した実行ファイルは、src-tauri\target\debug\ 内にある。しかしこの.exeを直接実行しようとすると、ウィンドウこそ開くが
127.0.0.1 により、接続が拒否されました。
プログラマーの魔法の使い方 Tauri デスクトップアプリ開発のはじめ方。
https://programwiz.org/2022/03/27/how-to-develop-desktop-app-with-tauri-framework/
VC++にGithub Copilotが統合され、Copilot Chatも使えるようになった。
自分の場合、元々Github Copilotの拡張機能を使っていたせいか、Copilotが動かなくなる現象に遭遇した。
既に使っていた場合は、拡張機能マネージャからGithub Copilotを削除する

再起動すれば標準機能でソースコード補完が動く。
Copilot Chatは [表示] → [GitHub Copilot チャット]で起動できる。

FLTKを使うと、設置したコントロール類がウィンドウサイズの変更と同時に移動・リサイズしてしまう。生成時の比率を維持するらしく使い勝手が悪いが、デフォルトでその機能を無効にする手段が見当たらなかったので、Event::Resize時にコントロールサイズを強制的に書き換えることで対処する。
use fltk::{app, prelude::*, window::Window, button::Button, enums::Event};
// 最初に指定したサイズ情報をボタンと一緒に扱うための構造体 struct WidgetWrap<T>{ x:i32, y:i32, w:i32, h:i32, btn:T, }
// WidgetWrapにボタンにメソッドを追加 impl<T:WidgetExt> WidgetWrap<T>{ /// @brief ウィジェットのサイズを含めてボタン管理 /// @param x_ x座標 /// @param y_ y座標 /// @param w_ 幅 /// @param h_ 高さ /// @param widget ウィジェットオブジェクト /// @return WidgetWrap<T> ウィジェット管理オブジェクト fn new(x_:i32,y_:i32,w_:i32,h_:i32,widget:T)->Self{ let mut _widget:WidgetWrap<T> = WidgetWrap{ x: x_, y: y_, w: w_, h: h_, btn:widget }; return _widget; } /// @brief ウィンドウサイズが変更されるときに呼び出し、ボタンの位置とサイズを強制的に維持する fn size_keep(&mut self){ self.btn.resize(self.x, self.y, self.w, self.h); } }
fn main() { let app = app::App::default(); let mut wind = Window::new(100, 100, 400, 300, "Resizable Window"); let mut _btn = Button::new( 160, 200, 80, 40, "The Button");// ボタン生成 let mut _btnwrap= WidgetWrap::new(// ボタンをサイズと一緒に管理する構造体に管理委譲 160, 200, 80, 40, _btn ); wind.end(); wind.resizable(&wind); wind.show(); wind.handle(move |_, ev| match ev { // ここでリサイズイベント時の処理を行う Event::Resize => { // ボタンの位置とサイズを保持 _btnwrap.size_keep(); true } _ => false, } ); app.run().unwrap(); }