Customise window decorations, add openfolder action to menu, fix toolbar colours
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
<mat-toolbar><span>BufPiv</span></mat-toolbar>
|
||||
<mat-toolbar data-tauri-drag-region color="secondary"
|
||||
><h1>BufPiv</h1></mat-toolbar
|
||||
>
|
||||
<mat-sidenav-container autosize>
|
||||
@if(selectedDirectory()) {
|
||||
<mat-sidenav mode="side" opened>
|
||||
<app-file-tree [files]="files()"></app-file-tree>
|
||||
</mat-sidenav>
|
||||
}
|
||||
<mat-sidenav-content>
|
||||
@if (!selectedDirectory) {
|
||||
<button mat-button (click)="selectDirectory()">Open Folder</button>
|
||||
@if (!selectedDirectory()) {
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
"
|
||||
>
|
||||
<button mat-button (click)="selectDirectory()" style="margin: auto">
|
||||
Open Folder
|
||||
</button>
|
||||
</div>
|
||||
}@else { }
|
||||
</mat-sidenav-content>
|
||||
<!-- TODO: Proto selector (add/remove proto definitinos, select definition for current file) on right side -->
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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<string | null>(null);
|
||||
protected files = signal<FileOrFolder[]>([]);
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ interface FileNode {
|
||||
<button mat-icon-button disabled></button>
|
||||
{{ node.file.name }}
|
||||
</mat-tree-node>
|
||||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild">
|
||||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodeToggle>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTreeNodePadding
|
||||
@@ -51,7 +51,7 @@ interface FileNode {
|
||||
chevron_right }
|
||||
</mat-icon>
|
||||
</button>
|
||||
{{ node.name }}
|
||||
{{ node.file.name }}
|
||||
</mat-tree-node>
|
||||
</mat-tree>`,
|
||||
styleUrl: './file-tree.component.scss',
|
||||
|
||||
1
src/app/messages/openfolder.message.ts
Normal file
1
src/app/messages/openfolder.message.ts
Normal file
@@ -0,0 +1 @@
|
||||
export interface OpenFolderMessage {}
|
||||
@@ -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,
|
||||
),
|
||||
));
|
||||
@@ -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,
|
||||
|
||||
10
src/theme.scss
Normal file
10
src/theme.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
@use "@angular/material" as mat;
|
||||
|
||||
$rose-theme: mat.define-theme(
|
||||
(
|
||||
color: (
|
||||
theme-type: dark,
|
||||
primary: mat.$rose-palette,
|
||||
),
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user