From ecdd4f7080a96ee2886bdc3f7524933300b48e8c Mon Sep 17 00:00:00 2001 From: Michael Pivato Date: Sat, 8 Jun 2024 16:30:05 +0930 Subject: [PATCH] Customise window decorations, add openfolder action to menu, fix toolbar colours --- src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 93 +++++++++++++++ src-tauri/tauri.conf.json | 52 +++++++-- src/app/app.component.html | 21 +++- src/app/app.component.scss | 6 + src/app/app.component.ts | 77 +++++++++++-- src/app/file-tree/file-tree.component.ts | 4 +- src/app/messages/openfolder.message.ts | 1 + src/m3-theme.scss | 141 ----------------------- src/styles.scss | 13 ++- src/theme.scss | 10 ++ 11 files changed, 253 insertions(+), 167 deletions(-) create mode 100644 src/app/messages/openfolder.message.ts delete mode 100644 src/m3-theme.scss create mode 100644 src/theme.scss diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8f3eeda..f74a329 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.5.2", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.6.5", features = [ "dialog-all", "fs-all"] } +tauri = { version = "1.6.5", features = [ "window-start-dragging", "dialog-all", "fs-all"] } [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e6ad770..4d65897 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,8 +1,101 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use std::any::Any; + +use tauri::{AboutMetadata, CustomMenuItem, Menu, MenuEntry, MenuItem, Submenu}; + +#[derive(Clone, serde::Serialize)] +struct OpenFolderMessage {} + fn main() { + let menu = build_menu("BufPiv"); tauri::Builder::default() + .menu(menu) + .on_menu_event(|event| match event.menu_item_id() { + "openfolder" => { + event + .window() + .emit("openfolder", OpenFolderMessage {}) + .expect("Failed to emit openfolder event"); + } + _ => {} + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } + +fn build_menu(app_name: &str) -> Menu { + let mut menu = Menu::new(); + #[cfg(target_os = "macos")] + { + menu = menu.add_submenu(Submenu::new( + app_name, + Menu::new() + .add_native_item(MenuItem::About( + app_name.to_string(), + AboutMetadata::default(), + )) + .add_native_item(MenuItem::Separator) + .add_native_item(MenuItem::Services) + .add_native_item(MenuItem::Separator) + .add_native_item(MenuItem::Hide) + .add_native_item(MenuItem::HideOthers) + .add_native_item(MenuItem::ShowAll) + .add_native_item(MenuItem::Separator) + .add_native_item(MenuItem::Quit), + )); + } + let open_folder = + CustomMenuItem::new("openfolder".to_string(), "Open Folder").accelerator("cmd+shift+O"); + let file_submenu = Menu::new() + .add_item(open_folder) + .add_native_item(MenuItem::CloseWindow); + #[cfg(not(target_os = "macos"))] + { + file_submenu = file_submenu.add_native_item(MenuItem::Quit); + } + menu = menu.add_submenu(Submenu::new("File", file_submenu)); + + #[cfg(not(target_os = "linux"))] + let mut edit_menu = Menu::new(); + #[cfg(target_os = "macos")] + { + edit_menu = edit_menu.add_native_item(MenuItem::Undo); + edit_menu = edit_menu.add_native_item(MenuItem::Redo); + edit_menu = edit_menu.add_native_item(MenuItem::Separator); + } + #[cfg(not(target_os = "linux"))] + { + edit_menu = edit_menu.add_native_item(MenuItem::Cut); + edit_menu = edit_menu.add_native_item(MenuItem::Copy); + edit_menu = edit_menu.add_native_item(MenuItem::Paste); + } + #[cfg(target_os = "macos")] + { + edit_menu = edit_menu.add_native_item(MenuItem::SelectAll); + } + #[cfg(not(target_os = "linux"))] + { + menu = menu.add_submenu(Submenu::new("Edit", edit_menu)); + } + #[cfg(target_os = "macos")] + { + menu = menu.add_submenu(Submenu::new( + "View", + Menu::new().add_native_item(MenuItem::EnterFullScreen), + )); + } + + let mut window_menu = Menu::new(); + window_menu = window_menu.add_native_item(MenuItem::Minimize); + #[cfg(target_os = "macos")] + { + window_menu = window_menu.add_native_item(MenuItem::Zoom); + window_menu = window_menu.add_native_item(MenuItem::Separator); + } + window_menu = window_menu.add_native_item(MenuItem::CloseWindow); + menu = menu.add_submenu(Submenu::new("Window", window_menu)); + + menu +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index c9de89f..6c95f89 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ "distDir": "../dist" }, "package": { - "productName": "Piv's Buf", + "productName": "BufPiv", "version": "0.0.0" }, "tauri": { @@ -18,16 +18,48 @@ }, "dialog": { "all": true + }, + "window": { + "all": false, + "center": false, + "close": false, + "create": false, + "hide": false, + "maximize": false, + "minimize": false, + "print": false, + "requestUserAttention": false, + "setAlwaysOnTop": false, + "setClosable": false, + "setContentProtected": false, + "setCursorGrab": false, + "setCursorIcon": false, + "setCursorPosition": false, + "setCursorVisible": false, + "setDecorations": false, + "setFocus": false, + "setFullscreen": false, + "setIcon": false, + "setIgnoreCursorEvents": false, + "setMaxSize": false, + "setMaximizable": false, + "setMinSize": false, + "setMinimizable": false, + "setPosition": false, + "setResizable": false, + "setSize": false, + "setSkipTaskbar": false, + "setTitle": false, + "show": false, + "startDragging": true, + "unmaximize": false, + "unminimize": false } }, "bundle": { "active": true, "category": "DeveloperTool", - "copyright": "", - "deb": { - "depends": [] - }, - "externalBin": [], + "copyright": "Michael Pivato 2024", "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -36,7 +68,7 @@ "icons/icon.ico" ], "identifier": "dev.michaelpivato", - "longDescription": "", + "longDescription": "Create and edit protobuf and json files based on protobuf definitions.", "macOS": { "entitlements": null, "exceptionDomain": "", @@ -64,8 +96,10 @@ "fullscreen": false, "height": 600, "resizable": true, - "title": "Piv's Buf", - "width": 800 + "title": "BufPiv", + "hiddenTitle": true, + "width": 800, + "titleBarStyle": "Overlay" } ] } diff --git a/src/app/app.component.html b/src/app/app.component.html index 1b95e64..7fa7c18 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,11 +1,26 @@ -BufPiv +

BufPiv

+ @if(selectedDirectory()) { + } - @if (!selectedDirectory) { - + @if (!selectedDirectory()) { +
+ +
}@else { }
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index c4d7999..5011c3d 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -3,6 +3,12 @@ flex-direction: column; height: 100%; } + +mat-toolbar { + flex: 0 0 auto; + padding-top: 24px; + --mat-toolbar-standard-height: 70px; +} mat-sidenav-container { width: 100%; height: 100%; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 042baaf..89da95e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,11 @@ import { CommonModule } from '@angular/common'; -import { Component, signal } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + OnDestroy, + OnInit, + signal, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatSidenavModule } from '@angular/material/sidenav'; import { RouterOutlet } from '@angular/router'; @@ -12,6 +18,8 @@ import { FileTreeComponent, } from './file-tree/file-tree.component'; import { MatIconModule } from '@angular/material/icon'; +import { listen, UnlistenFn } from '@tauri-apps/api/event'; +import { OpenFolderMessage } from './messages/openfolder.message'; @Component({ selector: 'app-root', @@ -30,9 +38,26 @@ import { MatIconModule } from '@angular/material/icon'; ], }) export class AppComponent { - protected selectedDirectory: string | null = null; + protected selectedDirectory = signal(null); protected files = signal([]); + private unlisten?: UnlistenFn; + + async ngOnInit() { + this.unlisten = await listen( + 'openfolder', + async (event: OpenFolderMessage) => { + await this.selectDirectory(); + } + ); + } + + async ngOnDestroy() { + if (this.unlisten) { + this.unlisten(); + } + } + async selectDirectory() { const selectedDirectory = await open({ directory: true, @@ -40,22 +65,58 @@ export class AppComponent { }); if (Array.isArray(selectedDirectory)) { message('Only a single folder can be selected at a time'); + return; } else { - this.selectedDirectory = selectedDirectory; + this.selectedDirectory.set(selectedDirectory); } - if (this.selectedDirectory) { - const entries = await readDir(this.selectedDirectory, { + if (selectedDirectory) { + const entries = await readDir(selectedDirectory, { recursive: true, }); - this.files.set(entries.map((entry) => this.mapEntry(entry))); + const splitNumbers = /(\d)+|(\D)+/; + this.files.set( + entries + .sort(this.sortFiles(splitNumbers)) + .map((entry) => this.mapEntry(entry, splitNumbers)) + ); } } - private mapEntry(entry: FileEntry): FileOrFolder { + private mapEntry(entry: FileEntry, splitNumbers: RegExp): FileOrFolder { return { isDirectory: entry.children != null, name: entry.name || '', - children: entry.children?.map((entry) => this.mapEntry(entry)), + children: entry.children?.map((entry) => + this.mapEntry(entry, splitNumbers) + ), }; } + + private sortFiles = + (splitNumbers: RegExp) => (a: FileEntry, b: FileEntry) => { + return a.name?.localeCompare(b.name ?? '') ?? 0; + // if (!a.name) { + // return -1; + // } + // if (!b.name) { + // return 1; + // } + // const aSplit = splitNumbers.exec(a.name); + // const bSplit = splitNumbers.exec(b.name); + // if (!aSplit) { + // return -1; + // } + // if (!bSplit) { + // return 1; + // } + // for ( + // let i = 0; + // i < (aSplit.length > bSplit.length ? bSplit.length : aSplit.length); + // i++ + // ) {} + // // TODO: Alternative is to iterate through characters in the strings to find numbers, if there isn't a number then return + // // locale compare? + // // TODO: finish this + // return 0; + }; } diff --git a/src/app/file-tree/file-tree.component.ts b/src/app/file-tree/file-tree.component.ts index f98faf2..dc4a908 100644 --- a/src/app/file-tree/file-tree.component.ts +++ b/src/app/file-tree/file-tree.component.ts @@ -40,7 +40,7 @@ interface FileNode { {{ node.file.name }} - + - {{ node.name }} + {{ node.file.name }} `, styleUrl: './file-tree.component.scss', diff --git a/src/app/messages/openfolder.message.ts b/src/app/messages/openfolder.message.ts new file mode 100644 index 0000000..97174b0 --- /dev/null +++ b/src/app/messages/openfolder.message.ts @@ -0,0 +1 @@ +export interface OpenFolderMessage {} diff --git a/src/m3-theme.scss b/src/m3-theme.scss deleted file mode 100644 index 55f4d04..0000000 --- a/src/m3-theme.scss +++ /dev/null @@ -1,141 +0,0 @@ -// This file was generated by running 'ng generate @angular/material:m3-theme'. -// Proceed with caution if making changes to this file. - -@use 'sass:map'; -@use '@angular/material' as mat; - -// Note: Color palettes are generated from primary: #DF6B85, secondary: #AE868D, tertiary: #B48858, neutral: #988E8F -$_palettes: ( - primary: ( - 0: #000000, - 10: #3f0015, - 20: #630828, - 25: #721633, - 30: #82233e, - 35: #912f49, - 40: #a13a54, - 50: #c0536c, - 60: #e06c86, - 70: #ff86a0, - 80: #ffb2bf, - 90: #ffd9de, - 95: #ffecee, - 98: #fff8f7, - 99: #fffbff, - 100: #ffffff, - ), - secondary: ( - 0: #000000, - 10: #3f0017, - 20: #5f112b, - 25: #6d1d35, - 30: #7c2940, - 35: #8b344c, - 40: #9a4058, - 50: #b95870, - 60: #d87189, - 70: #f78aa3, - 80: #ffb1c0, - 90: #ffd9df, - 95: #ffecee, - 98: #fff8f7, - 99: #fffbff, - 100: #ffffff, - ), - tertiary: ( - 0: #000000, - 10: #2b1700, - 20: #482900, - 25: #573300, - 30: #673d00, - 35: #774700, - 40: #885200, - 50: #aa6800, - 60: #c98121, - 70: #e89b3a, - 80: #ffb868, - 90: #ffddbb, - 95: #ffeedf, - 98: #fff8f4, - 99: #fffbff, - 100: #ffffff, - ), - neutral: ( - 0: #000000, - 10: #3f0018, - 20: #5f112c, - 25: #6d1d37, - 30: #7c2942, - 35: #8b344e, - 40: #9a4059, - 50: #b85872, - 60: #d7718b, - 70: #f78aa5, - 80: #ffb1c2, - 90: #ffd9e0, - 95: #ffecee, - 98: #fff8f7, - 99: #fffbff, - 100: #ffffff, - ), - neutral-variant: ( - 0: #000000, - 10: #24191a, - 20: #3a2d2f, - 25: #46383a, - 30: #524345, - 35: #5e4f51, - 40: #6a5a5c, - 50: #847375, - 60: #9f8c8e, - 70: #baa6a9, - 80: #d6c2c4, - 90: #f3dddf, - 95: #ffecee, - 98: #fff8f7, - 99: #fffbff, - 100: #ffffff, - ), - error: ( - 0: #000000, - 10: #410002, - 20: #690005, - 25: #7e0007, - 30: #93000a, - 35: #a80710, - 40: #ba1a1a, - 50: #de3730, - 60: #ff5449, - 70: #ff897d, - 80: #ffb4ab, - 90: #ffdad6, - 95: #ffedea, - 98: #fff8f7, - 99: #fffbff, - 100: #ffffff, - ), -); - -$_rest: ( - secondary: map.get($_palettes, secondary), - neutral: map.get($_palettes, neutral), - neutral-variant: map.get($_palettes, neutral-variant), - error: map.get($_palettes, error), -); -$_primary: map.merge(map.get($_palettes, primary), $_rest); -$_tertiary: map.merge(map.get($_palettes, tertiary), $_rest); - -$light-theme: mat.define-theme(( - color: ( - theme-type: light, - primary: $_primary, - tertiary: $_tertiary, - ), -)); -$dark-theme: mat.define-theme(( - color: ( - theme-type: dark, - primary: $_primary, - tertiary: $_tertiary, - ), -)); \ No newline at end of file diff --git a/src/styles.scss b/src/styles.scss index 136375c..ac94bab 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,10 +1,17 @@ -@use '@angular/material' as mat; -@use './m3-theme.scss'; +@use "@angular/material" as mat; +@use "./theme.scss"; @include mat.core(); +@mixin custom-colours($theme) { + .mat-toolbar { + background-color: mat.get-theme-color($theme, primary-container); + } +} + html { - @include mat.all-component-themes(m3-theme.$dark-theme); + @include mat.all-component-themes(theme.$rose-theme); + @include custom-colours(theme.$rose-theme); } html, diff --git a/src/theme.scss b/src/theme.scss new file mode 100644 index 0000000..a94eeb8 --- /dev/null +++ b/src/theme.scss @@ -0,0 +1,10 @@ +@use "@angular/material" as mat; + +$rose-theme: mat.define-theme( + ( + color: ( + theme-type: dark, + primary: mat.$rose-palette, + ), + ) +);