feat: Add custom window frame option + some tweaks
This commit is contained in:
parent
eafd9eed58
commit
30b2de6129
17 changed files with 155 additions and 27 deletions
|
|
@ -21,6 +21,8 @@
|
|||
"core:window:allow-maximize",
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-internal-toggle-maximize",
|
||||
"core:window:allow-set-decorations",
|
||||
"core:window:allow-set-title-bar-style",
|
||||
"core:webview:allow-internal-toggle-devtools",
|
||||
|
||||
"core:event:default",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use interpulse::api::minecraft::Version;
|
||||
use onelauncher::data::Settings;
|
||||
use onelauncher::settings;
|
||||
use tauri::AppHandle;
|
||||
use tauri::Manager;
|
||||
|
||||
mod cluster;
|
||||
pub use crate::api::commands::cluster::*;
|
||||
|
|
@ -76,7 +78,9 @@ macro_rules! collect_commands {
|
|||
sync_cluster_packages_by_type,
|
||||
// Updater
|
||||
check_for_update,
|
||||
install_update
|
||||
install_update,
|
||||
// Other
|
||||
set_window_style,
|
||||
]
|
||||
}};
|
||||
}
|
||||
|
|
@ -100,3 +104,11 @@ pub async fn get_settings() -> Result<Settings, String> {
|
|||
pub async fn set_settings(settings: Settings) -> Result<(), String> {
|
||||
Ok(settings::set(settings).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub fn set_window_style(handle: AppHandle, custom: bool) -> Result<(), String> {
|
||||
let window = handle.get_webview_window("main").unwrap();
|
||||
onelauncher::utils::window::set_window_styling(&window, custom)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use api::statics::get_program_info;
|
||||
use onelauncher::utils::window::set_window_styling;
|
||||
use tauri::{Emitter, Manager};
|
||||
|
||||
pub mod api;
|
||||
|
|
@ -91,7 +92,24 @@ pub async fn run_app<F: FnOnce(&tauri::AppHandle<tauri::Wry>) + Send + 'static>(
|
|||
|
||||
fn setup(handle: &tauri::AppHandle) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let win = handle.get_webview_window("main").unwrap();
|
||||
win.show().unwrap();
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let state = match onelauncher::State::get().await {
|
||||
Ok(state) => state,
|
||||
Err(err) => {
|
||||
tracing::error!("{err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let settings = state.settings.read().await;
|
||||
|
||||
if let Err(err) = set_window_styling(&win, settings.custom_frame) {
|
||||
tracing::error!(err);
|
||||
};
|
||||
|
||||
win.show().unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ function SettingsRow(props: SettingsRowProps) {
|
|||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-2">
|
||||
<h3 class="text-lg">{props.title}</h3>
|
||||
<h3 class="text-lg capitalize">{props.title}</h3>
|
||||
<p class="text-wrap text-sm">{props.description}</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { JSX } from 'solid-js';
|
||||
import { Window } from '@tauri-apps/api/window';
|
||||
import { ChevronLeftIcon, Maximize02Icon, MinusIcon, XCloseIcon } from '@untitled-theme/icons-solid';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { onMount } from 'solid-js';
|
||||
import { onMount, Show } from 'solid-js';
|
||||
import type { JSX } from 'solid-js';
|
||||
import Button from './base/Button';
|
||||
import Modal, { createModal } from './overlay/Modal';
|
||||
|
||||
|
|
@ -51,17 +51,19 @@ function WindowFrame() {
|
|||
});
|
||||
|
||||
return (
|
||||
<div class="z-[99999] h-8 w-screen flex flex-row items-center justify-between gap-0.5 bg-page-elevated pr-0.5" data-tauri-drag-region>
|
||||
<div class="flex flex-row items-center">
|
||||
<TitlebarButton icon={ChevronLeftIcon} onClick={() => history.back()} />
|
||||
</div>
|
||||
<Show when={settings().custom_frame}>
|
||||
<div class="z-[99999] h-8 w-screen flex flex-row items-center justify-between gap-0.5 bg-page-elevated pr-0.5" data-tauri-drag-region>
|
||||
<div class="flex flex-row items-center">
|
||||
<TitlebarButton icon={ChevronLeftIcon} onClick={() => history.back()} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-end">
|
||||
<TitlebarButton icon={MinusIcon} onClick={() => minimize()} />
|
||||
<TitlebarButton icon={Maximize02Icon} onClick={() => maximize()} />
|
||||
<TitlebarButton danger icon={XCloseIcon} onClick={() => quit()} />
|
||||
<div class="flex flex-row items-center justify-end">
|
||||
<TitlebarButton icon={MinusIcon} onClick={() => minimize()} />
|
||||
<TitlebarButton icon={Maximize02Icon} onClick={() => maximize()} />
|
||||
<TitlebarButton danger icon={XCloseIcon} onClick={() => quit()} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.button {
|
||||
@apply py-1.5 px-3 rounded-lg flex flex-row justify-center items-center gap-x-2.5;
|
||||
@apply py-1.5 px-3 rounded-lg flex flex-row justify-center items-center gap-x-2.5 h-9.5;
|
||||
|
||||
&__primary, &__iconPrimary {
|
||||
@apply text-onbrand hover:text-onbrand-hover active:text-onbrand-pressed disabled:text-onbrand-disabled;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Accessor, JSX, ParentProps } from 'solid-js';
|
||||
import { createEffect, createSignal, mergeProps, on, splitProps, untrack } from 'solid-js';
|
||||
import type { Accessor, JSX, ParentProps } from 'solid-js';
|
||||
import styles from './Button.module.scss';
|
||||
|
||||
type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
|
||||
|
|
|
|||
51
apps/frontend/src/ui/components/content/ProviderIcon.tsx
Normal file
51
apps/frontend/src/ui/components/content/ProviderIcon.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import ModrinthImage from '~assets/logos/modrinth.svg?component-solid';
|
||||
import { type Component, type JSX, Match, Show, splitProps, Switch } from 'solid-js';
|
||||
import type { ManagedPackage, Providers } from '@onelauncher/client/bindings';
|
||||
|
||||
export function getProviderLogoElement(provider: ManagedPackage | Providers): string | Component {
|
||||
const providerName = (typeof provider === 'string' ? provider : provider.provider)?.toLowerCase() as Lowercase<Providers>;
|
||||
|
||||
// [provider logo, is svg]
|
||||
const mapping: Record<Lowercase<Providers>, string | Component> = {
|
||||
modrinth: ModrinthImage,
|
||||
};
|
||||
|
||||
return mapping[providerName];
|
||||
}
|
||||
|
||||
type ProviderIconProps = JSX.HTMLAttributes<HTMLImageElement> & {
|
||||
provider: Providers | undefined;
|
||||
};
|
||||
|
||||
function ProviderIcon(props: ProviderIconProps) {
|
||||
const [split, rest] = splitProps(props, ['provider', 'class']);
|
||||
|
||||
const Element = () => {
|
||||
const value = getProviderLogoElement(split.provider!);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={typeof value === 'string'}>
|
||||
<img {...rest} {...(split.class ? { class: split.class } : {})} alt={`${split.provider}'s logo`} src={value as string} />
|
||||
</Match>
|
||||
<Match when>
|
||||
{value}
|
||||
</Match>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Show
|
||||
children={(
|
||||
<Element />
|
||||
)}
|
||||
fallback={(
|
||||
<div class={`bg-gray-05 ${split.class || ''}`} {...rest as JSX.HTMLAttributes<HTMLDivElement>} />
|
||||
)}
|
||||
when={split.provider !== undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProviderIcon;
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Cluster, Loader } from '@onelauncher/client/bindings';
|
||||
import FabricImage from '~assets/logos/fabric.png';
|
||||
import ForgeImage from '~assets/logos/forge.png';
|
||||
import QuiltImage from '~assets/logos/quilt.png';
|
||||
import VanillaImage from '~assets/logos/vanilla.png';
|
||||
import { type JSX, splitProps } from 'solid-js';
|
||||
import type { Cluster, Loader } from '@onelauncher/client/bindings';
|
||||
|
||||
export function getLoaderLogoSrc(loader: Cluster | Loader): string {
|
||||
const loaderName = (typeof loader === 'string' ? loader : loader.meta.loader)?.toLowerCase() as Loader;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { ManagedPackage } from '@onelauncher/client/bindings';
|
||||
import { ChevronRightIcon } from '@untitled-theme/icons-solid';
|
||||
import OneConfigLogo from '~assets/logos/oneconfig.svg?component-solid';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import SearchResultsContainer from '~ui/components/content/SearchResults';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
import { onMount, Show } from 'solid-js';
|
||||
import type { ManagedPackage } from '@onelauncher/client/bindings';
|
||||
import { BrowserContent } from './BrowserRoot';
|
||||
|
||||
function BrowserMain() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Route, useLocation } from '@solidjs/router';
|
||||
import { SearchMdIcon, Settings01Icon } from '@untitled-theme/icons-solid';
|
||||
import { SearchMdIcon } from '@untitled-theme/icons-solid';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
import ProviderIcon from '~ui/components/content/ProviderIcon';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
import { PROVIDERS } from '~utils';
|
||||
import { browserCategories } from '~utils/browser';
|
||||
|
|
@ -119,7 +120,6 @@ function BrowserSidebar() {
|
|||
<Button
|
||||
buttonStyle="secondary"
|
||||
children={controller.cluster()?.meta.name || 'None'}
|
||||
iconLeft={<Settings01Icon />}
|
||||
onClick={controller.displayClusterSelector}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -135,6 +135,7 @@ function BrowserSidebar() {
|
|||
<For each={PROVIDERS}>
|
||||
{provider => (
|
||||
<Dropdown.Row>
|
||||
<ProviderIcon class="h-4 w-4" provider={provider} />
|
||||
{provider}
|
||||
</Dropdown.Row>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { useBeforeLeave } from '@solidjs/router';
|
||||
import { PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-solid';
|
||||
import { Window } from '@tauri-apps/api/window';
|
||||
import { Monitor01Icon, PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-solid';
|
||||
import { bridge } from '~imports';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import Toggle from '~ui/components/base/Toggle';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
|
|
@ -20,10 +22,14 @@ function SettingsAppearance() {
|
|||
setShouldReload(false);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
if (settings().custom_frame !== undefined)
|
||||
bridge.commands.setWindowStyle(settings().custom_frame!);
|
||||
});
|
||||
|
||||
saveOnLeave(() => ({
|
||||
disable_animations: settings().disable_animations!,
|
||||
custom_frame: settings().custom_frame!,
|
||||
}));
|
||||
|
||||
return (
|
||||
|
|
@ -59,6 +65,21 @@ function SettingsAppearance() {
|
|||
</Dropdown>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
description="Uses custom window frame for the launcher."
|
||||
icon={<Monitor01Icon />}
|
||||
title="Custom Window Frame"
|
||||
>
|
||||
<Toggle
|
||||
checked={() => settings().custom_frame ?? true}
|
||||
onChecked={(value) => {
|
||||
settings().custom_frame = value;
|
||||
Window.getCurrent().setDecorations(value);
|
||||
// setShouldReload(true);
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
description="Disables all animations in the launcher."
|
||||
icon={<Speedometer04Icon />}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { BrowserListView, PackageType, Providers } from '@onelauncher/client/bindings';
|
||||
|
||||
export const BROWSER_VIEWS: BrowserListView[] = ['grid', 'list', 'preview'] as const;
|
||||
export const BROWSER_VIEWS: BrowserListView[] = ['grid', 'list'] as const;
|
||||
|
||||
export interface CategoryItem {
|
||||
display: string;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { Cluster, ImportType, License, Loader, PackageType, Providers, VersionType } from '@onelauncher/client/bindings';
|
||||
import { DurationFormat } from '@formatjs/intl-durationformat';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import type { Cluster, ImportType, License, Loader, PackageType, Providers, VersionType } from '@onelauncher/client/bindings';
|
||||
|
||||
export function setAsyncTimeout(ms: number): Promise<void>;
|
||||
export function setAsyncTimeout(callback: () => any, ms: number): Promise<void>;
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ pub struct Settings {
|
|||
/// Whether or not to minimize the launcher upon a game launch.
|
||||
#[serde(default)]
|
||||
pub hide_on_launch: bool,
|
||||
/// Enable/disable advanced rendering and window decorations.
|
||||
/// Enable/disable custom window decorations.
|
||||
#[serde(default)]
|
||||
pub rendering: bool,
|
||||
pub custom_frame: bool,
|
||||
/// Completed onboarding.
|
||||
#[serde(default)]
|
||||
pub onboarding_completed: bool,
|
||||
|
|
@ -127,7 +127,7 @@ impl Settings {
|
|||
debug_mode: false,
|
||||
config_dir: Directories::init_settings_dir(),
|
||||
hide_on_launch: false,
|
||||
rendering: true,
|
||||
custom_frame: true,
|
||||
onboarding_completed: false,
|
||||
};
|
||||
|
||||
|
|
@ -155,7 +155,6 @@ pub enum BrowserListView {
|
|||
#[default]
|
||||
Grid,
|
||||
List,
|
||||
Preview,
|
||||
}
|
||||
|
||||
/// A `OneLauncher` theme managed by the GUI.
|
||||
|
|
|
|||
|
|
@ -8,3 +8,6 @@
|
|||
pub mod http;
|
||||
pub mod java;
|
||||
pub mod watcher;
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
pub mod window;
|
||||
|
|
|
|||
19
packages/core/src/utils/window.rs
Normal file
19
packages/core/src/utils/window.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use tauri::WebviewWindow;
|
||||
|
||||
pub fn set_window_styling(
|
||||
win: &WebviewWindow,
|
||||
custom: bool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
win.set_decorations(!custom)?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if custom {
|
||||
win.set_title_bar_style(tauri_utils::TitleBarStyle::Overlay)?;
|
||||
} else {
|
||||
win.set_title_bar_style(tauri_utils::TitleBarStyle::Visible)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue