Skip to content

Commit

Permalink
feat: progress on initializing window from config
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-berger committed Aug 23, 2024
1 parent d3764b5 commit 7b62364
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 88 deletions.
5 changes: 4 additions & 1 deletion packages/client-api/src/desktop/current-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export async function setWindowStyles(styles: Partial<WindowStyles>) {
]);
}

async function setWindowZOrder(window: Window, zOrder?: WindowZOrder) {
export async function setWindowZOrder(
window: Window,
zOrder?: WindowZOrder,
) {
if (zOrder === 'always_on_bottom') {
await window.setAlwaysOnBottom(true);
} else if (zOrder === 'always_on_top') {
Expand Down
30 changes: 10 additions & 20 deletions packages/client-api/src/desktop/desktop-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,24 @@ import {
} from '@tauri-apps/api/core';

import { createLogger } from '../utils';
import type { ProviderConfig } from '~/user-config';
import type { OpenWindowArgs } from './shared';
import type { WindowState } from './shared';
import type { ProviderConfig } from '~/providers';

const logger = createLogger('desktop-commands');

/**
* Reads config file from disk. Creates file if it doesn't exist.
* Get state associated with the given {@link windowId}.
*/
export function readConfigFile(): Promise<string> {
return invoke<string>('read_config_file');
}

/**
* Get args used to open the window with the {@link windowLabel}.
*/
export function getInitialState(
windowLabel: string,
): Promise<OpenWindowArgs | null> {
return invoke<OpenWindowArgs | null>('get_open_window_args', {
windowLabel,
export function getWindowState(
windowId: string,
): Promise<WindowState | null> {
return invoke<WindowState | null>('get_window_state', {
windowId,
});
}

export function openWindow(
windowId: string,
args: Record<string, string> = {},
): Promise<void> {
return invoke<void>('open_window', { windowId, args });
export function openWindow(configPath: string): Promise<void> {
return invoke<void>('open_window', { configPath });
}

// TODO: Add support for only fetching selected variables.
Expand Down
4 changes: 2 additions & 2 deletions packages/client-api/src/desktop/get-open-window-args.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getCurrentWindow } from '@tauri-apps/api/window';

import { getInitialState } from './desktop-commands';
import { getWindowState } from './desktop-commands';

let promise: Promise<any> | null = null;

Expand All @@ -13,5 +13,5 @@ async function fetchOpenWindowArgs() {
return window.__ZEBAR_INITIAL_STATE;
}

return getInitialState(getCurrentWindow().label);
return getWindowState(getCurrentWindow().label);
}
2 changes: 1 addition & 1 deletion packages/client-api/src/desktop/shared/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './monitor-info.model';
export * from './open-window-args.model';
export * from './window-state.model';

This file was deleted.

9 changes: 9 additions & 0 deletions packages/client-api/src/desktop/shared/window-state.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { WindowConfig } from '~/user-config';

export interface WindowState {
windowId: string;

config: WindowConfig;

configPath: string;
}
32 changes: 25 additions & 7 deletions packages/client-api/src/init.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { getCurrentWindow } from '@tauri-apps/api/window';
import { join } from '@tauri-apps/api/path';
import { createRoot, getOwner, runWithOwner } from 'solid-js';

import { getInitialState, openWindow, showErrorDialog } from './desktop';
import {
getWindowState,
openWindow,
setWindowZOrder,
showErrorDialog,
} from './desktop';
import { createLogger } from '~/utils';
import type { ZebarContext } from './zebar-context.model';
import { createProvider } from './providers';
Expand Down Expand Up @@ -32,9 +38,9 @@ export async function init(
try {
const currentWindow = getCurrentWindow();

const initialState =
const windowState =
window.__ZEBAR_INITIAL_STATE ??
(await getInitialState(currentWindow.label));
(await getWindowState(currentWindow.label));

// Load default CSS unless explicitly disabled.
if (options?.includeDefaultCss !== false) {
Expand All @@ -45,13 +51,25 @@ export async function init(

// @ts-ignore - TODO
return {
// @ts-ignore - TODO
config: initialState.config,
openWindow,
openWindow: async (configPath: string) => {
const absolutePath = await join(
windowState.windowId,
'../',
configPath,
);

return openWindow(absolutePath);
},
createProvider: config => {
return createProvider(config, getOwner()!);
},
currentWindow: {},
currentWindow: {
...windowState,
tauri: currentWindow,
setZOrder: zOrder => {
return setWindowZOrder(currentWindow, zOrder);
},
},
allWindows: [],
currentMonitor: {},
allMonitors: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/client-api/src/shims.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
interface Window {
__TAURI__: any;
__ZEBAR_INITIAL_STATE: import('./desktop').OpenWindowArgs;
__ZEBAR_INITIAL_STATE: import('./desktop').WindowState;
}

declare module '*.css' {
Expand Down
5 changes: 4 additions & 1 deletion packages/client-api/src/zebar-context.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ export interface ZebarContext {
}

export interface ZebarWindow {
windowId: string;
config: WindowConfig;
configPath: string;
tauri: TauriWindow;
setZOrder: (zOrder: WindowZOrder) => Promise<void>;
setZOrder(zOrder: WindowZOrder): Promise<void>;
}

export interface Monitor {
Expand Down
18 changes: 0 additions & 18 deletions packages/desktop/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ pub enum CliCommand {
pub struct OpenWindowArgs {
/// Relative file path within the Zebar config directory.
pub config_path: String,

/// Arguments to pass to the window.
///
/// These become available via the `self` provider.
#[clap(short, long, num_args = 1.., value_parser=parse_open_args)]
pub args: Option<Vec<(String, String)>>,
}

#[derive(Args, Debug)]
Expand All @@ -63,15 +57,3 @@ pub fn print_and_exit(output: anyhow::Result<String>) {
}
}
}

/// Parses arguments passed to the `open` CLI command into a string tuple.
fn parse_open_args(
input: &str,
) -> anyhow::Result<(String, String), String> {
let mut parts = input.split('=');

match (parts.next(), parts.next()) {
(Some(key), Some(value)) => Ok((key.into(), value.into())),
_ => Err("Arguments must be of format KEY1=VAL1".into()),
}
}
17 changes: 17 additions & 0 deletions packages/desktop/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ impl Config {
Ok(())
}

/// Returns the window config at the given absolute path.
pub fn window_config_by_path(
&self,
config_path: &str,
) -> anyhow::Result<Option<WindowConfig>> {
let config_pathbuf = PathBuf::from(config_path).canonicalize()?;

let config_entry = self
.window_configs
.iter()
.find(|entry| entry.path == config_pathbuf);

let config = config_entry.map(|entry| entry.config.clone());

Ok(config)
}

/// Opens the config directory in the OS-dependent file explorer.
pub fn open_config_dir(&self) -> anyhow::Result<()> {
#[cfg(target_os = "windows")]
Expand Down
29 changes: 16 additions & 13 deletions packages/desktop/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![feature(async_closure)]
use std::{collections::HashMap, env};
use std::env;

use anyhow::Context;
use clap::Parser;
use config::Config;
use tauri::{Manager, State, Window};
Expand All @@ -25,23 +26,25 @@ mod sys_tray;
mod window_factory;

#[tauri::command]
async fn get_open_window_args(
window_label: String,
async fn get_window_state(
window_id: String,
window_factory: State<'_, WindowFactory>,
config: State<'_, Config>,
) -> anyhow::Result<Option<WindowState>, String> {
println!("{:?}", config.config_dir);
Ok(window_factory.state_by_window_label(window_label).await)
Ok(window_factory.state_by_id(&window_id).await)
}

#[tauri::command]
fn open_window(
async fn open_window(
config_path: String,
args: HashMap<String, String>,
config: State<'_, Config>,
window_factory: State<'_, WindowFactory>,
) -> anyhow::Result<(), String> {
// TODO: Pass in `WindowConfig`.
window_factory.try_open();
let window_config = config
.window_config_by_path(&config_path)
.map_err(|err| err.to_string())?
.context("Window config not found.")?;

window_factory.open_one(window_config);

Ok(())
}
Expand Down Expand Up @@ -149,7 +152,7 @@ fn start_app(cli: Cli) {

// CLI command is guaranteed to be an open command here.
if let CliCommand::Open(args) = cli.command {
app.state::<WindowFactory>().try_open();
app.state::<WindowFactory>().open_one();
}
},
))?;
Expand All @@ -161,7 +164,7 @@ fn start_app(cli: Cli) {
// Open window with the given args and initialize
// `WindowFactory` in Tauri state.
let window_factory = WindowFactory::new(app.handle());
window_factory.try_open();
window_factory.open_one();
app.manage(window_factory);

app.handle().plugin(tauri_plugin_shell::init())?;
Expand All @@ -179,7 +182,7 @@ fn start_app(cli: Cli) {
Ok(())
})
.invoke_handler(tauri::generate_handler![
get_open_window_args,
get_window_state,
open_window,
listen_provider,
unlisten_provider,
Expand Down
44 changes: 25 additions & 19 deletions packages/desktop/src/window_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tauri::{AppHandle, WebviewUrl, WebviewWindowBuilder};
use tokio::{sync::Mutex, task};
use tracing::{error, info};

use crate::common::WindowExt;
use crate::{common::WindowExt, config::WindowConfig};

/// Manages the creation of Zebar windows.
pub struct WindowFactory {
Expand All @@ -30,10 +30,16 @@ pub struct WindowFactory {
#[derive(Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct WindowState {
/// Unique identifier for the window.
///
/// Used as the window label.
pub window_id: String,
pub window_label: String,
pub args: HashMap<String, String>,
pub env: HashMap<String, String>,

/// User-defined config for the window.
pub config: WindowConfig,

/// Absolute path to the window's config file.
pub config_path: String,
}

impl WindowFactory {
Expand All @@ -45,23 +51,27 @@ impl WindowFactory {
}
}

/// TODO: Pass in `WindowConfig`.
pub fn try_open(&self) {
pub fn open_all(&self, configs: Vec<WindowConfig>) {
for config in configs {
self.open_one(config);
}
}

pub fn open_one(&self, config: WindowConfig) {
let app_handle = self.app_handle.clone();
let window_states = self.window_states.clone();
let app_handle = app_handle.clone();
let window_count = self.window_count.clone();

task::spawn(async move {
// Increment number of windows.
let new_count = window_count.fetch_add(1, Ordering::Relaxed) + 1;

let open_res = Self::open(&app_handle, new_count);
let open_res = Self::create_window(&app_handle, new_count, config);

match open_res {
Ok(state) => {
let mut window_states = window_states.lock().await;
window_states.insert(state.window_label.clone(), state);
window_states.insert(state.window_id.clone(), state);
}
Err(err) => {
error!("Failed to open window: {:?}", err);
Expand All @@ -70,9 +80,10 @@ impl WindowFactory {
});
}

fn open(
fn create_window(
app_handle: &AppHandle,
window_count: u32,
config: WindowConfig,
) -> anyhow::Result<WindowState> {
info!("Creating window #{}", window_count);

Expand All @@ -95,9 +106,7 @@ impl WindowFactory {

let state = WindowState {
window_id: window_count.to_string(),
window_label: window_count.to_string(),
args: HashMap::new(),
env: std::env::vars().collect(),
config,
};

_ = window.eval(&format!(
Expand All @@ -113,11 +122,8 @@ impl WindowFactory {
Ok(state)
}

/// Gets an open window's state by a given window label.
pub async fn state_by_window_label(
&self,
window_label: String,
) -> Option<WindowState> {
self.window_states.lock().await.get(&window_label).cloned()
/// Gets an open window's state by a given window ID.
pub async fn state_by_id(&self, window_id: &str) -> Option<WindowState> {
self.window_states.lock().await.get(window_id).cloned()
}
}

0 comments on commit 7b62364

Please sign in to comment.