mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-12 23:24:35 +01:00
working theming
This commit is contained in:
@@ -16,8 +16,9 @@
|
||||
"grasscutter_jar": "Set Grasscutter JAR",
|
||||
"java_path": "Set Custom Java Path",
|
||||
"grasscutter_with_game": "Automatically launch Grasscutter with game",
|
||||
"language": "Select Language (requires restart)",
|
||||
"background": "Set Custom Background (link or image file)"
|
||||
"language": "Select Language",
|
||||
"background": "Set Custom Background (link or image file)",
|
||||
"theme": "Set Theme"
|
||||
},
|
||||
"downloads": {
|
||||
"grasscutter_stable_data": "Download Grasscutter Stable Data",
|
||||
|
||||
@@ -50,6 +50,7 @@ fn main() {
|
||||
system_helpers::run_jar,
|
||||
system_helpers::open_in_browser,
|
||||
system_helpers::copy_file,
|
||||
system_helpers::install_location,
|
||||
proxy::set_proxy_addr,
|
||||
proxy::generate_ca_files,
|
||||
unzip::unzip,
|
||||
@@ -145,8 +146,8 @@ async fn req_get(url: String) -> String {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_theme_list() -> Vec<HashMap<String, String>> {
|
||||
let theme_loc = format!("{}/themes", system_helpers::install_location());
|
||||
async fn get_theme_list(dataDir: String) -> Vec<HashMap<String, String>> {
|
||||
let theme_loc = format!("{}/themes", dataDir);
|
||||
|
||||
// Ensure folder exists
|
||||
if !std::path::Path::new(&theme_loc).exists() {
|
||||
|
||||
@@ -77,6 +77,7 @@ pub fn copy_file(path: String, new_path: String) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn install_location() -> String {
|
||||
let mut exe_path = std::env::current_exe().unwrap();
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ import Game from './components/menu/Game'
|
||||
import RightBar from './components/RightBar'
|
||||
import { getConfigOption, setConfigOption } from '../utils/configuration'
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import { appDir, dataDir } from '@tauri-apps/api/path'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { getTheme, loadTheme } from '../utils/themes'
|
||||
|
||||
interface IProps {
|
||||
[key: string]: never;
|
||||
@@ -82,6 +83,13 @@ class App extends React.Component<IProps, IState> {
|
||||
const game_path = game_exe.substring(0, game_exe.replace(/\\/g, '/').lastIndexOf('/'))
|
||||
const root_path = game_path.substring(0, game_path.replace(/\\/g, '/').lastIndexOf('/'))
|
||||
|
||||
// Load a theme if it exists
|
||||
const theme = await getConfigOption('theme')
|
||||
if (theme) {
|
||||
const themeObj = await getTheme(theme)
|
||||
loadTheme(themeObj, document)
|
||||
}
|
||||
|
||||
if(!custom_bg || !/png|jpg|jpeg$/.test(custom_bg)) {
|
||||
if(game_path) {
|
||||
// Get the bg by invoking, then set the background to that bg.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import DirInput from '../common/DirInput'
|
||||
import Menu from './Menu'
|
||||
import Tr, { getLanguages } from '../../../utils/language'
|
||||
import './Options.css'
|
||||
import { setConfigOption, getConfig, getConfigOption } from '../../../utils/configuration'
|
||||
import Checkbox from '../common/Checkbox'
|
||||
import Divider from './Divider'
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import { getThemeList } from '../../../utils/themes'
|
||||
|
||||
import './Options.css'
|
||||
|
||||
interface IProps {
|
||||
closeFn: () => void;
|
||||
@@ -21,6 +23,8 @@ interface IState {
|
||||
language_options: { [key: string]: string }[],
|
||||
current_language: string
|
||||
bg_url_or_path: string
|
||||
themes: string[]
|
||||
theme: string
|
||||
}
|
||||
|
||||
export default class Options extends React.Component<IProps, IState> {
|
||||
@@ -34,7 +38,9 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
grasscutter_with_game: false,
|
||||
language_options: [],
|
||||
current_language: 'en',
|
||||
bg_url_or_path: ''
|
||||
bg_url_or_path: '',
|
||||
themes: ['default'],
|
||||
theme: ''
|
||||
}
|
||||
|
||||
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
||||
@@ -52,7 +58,9 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
grasscutter_with_game: config.grasscutter_with_game || false,
|
||||
language_options: languages,
|
||||
current_language: config.language || 'en',
|
||||
bg_url_or_path: config.customBackground || ''
|
||||
bg_url_or_path: config.customBackground || '',
|
||||
themes: (await getThemeList()).map(t => t.name),
|
||||
theme: config.theme || 'default'
|
||||
})
|
||||
|
||||
this.forceUpdate()
|
||||
@@ -70,8 +78,14 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
setConfigOption('java_path', value)
|
||||
}
|
||||
|
||||
setLanguage(value: string) {
|
||||
setConfigOption('language', value)
|
||||
async setLanguage(value: string) {
|
||||
await setConfigOption('language', value)
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
async setTheme(value: string) {
|
||||
await setConfigOption('theme', value)
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
async toggleGrasscutterWithGame() {
|
||||
@@ -140,6 +154,28 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className='OptionSection'>
|
||||
<div className='OptionLabel'>
|
||||
<Tr text="options.theme" />
|
||||
</div>
|
||||
<div className='OptionValue'>
|
||||
<select value={this.state.theme} onChange={(event) => {
|
||||
this.setTheme(event.target.value)
|
||||
}}>
|
||||
{this.state.themes.map(t => (
|
||||
<option
|
||||
key={t}
|
||||
value={t}>
|
||||
|
||||
{t}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<div className='OptionSection'>
|
||||
<div className='OptionLabel'>
|
||||
<Tr text="options.java_path" />
|
||||
|
||||
@@ -18,6 +18,7 @@ let defaultConfig: Configuration
|
||||
language: 'en',
|
||||
customBackground: '',
|
||||
cert_generated: false,
|
||||
theme: 'default'
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -37,6 +38,7 @@ export interface Configuration {
|
||||
language: string
|
||||
customBackground: string
|
||||
cert_generated: boolean
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export async function setConfigOption(key: string, value: any): Promise<void> {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { dataDir } from '@tauri-apps/api/path'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
|
||||
interface Theme {
|
||||
name: string
|
||||
@@ -24,10 +26,34 @@ interface ThemeList extends Theme {
|
||||
path: string
|
||||
}
|
||||
|
||||
const defaultTheme = {
|
||||
name: 'default',
|
||||
version: '1.0.0',
|
||||
description: 'Default theme',
|
||||
includes: {
|
||||
css: [],
|
||||
js: []
|
||||
},
|
||||
path: 'default'
|
||||
}
|
||||
export async function getThemeList() {
|
||||
// Do some invoke to backend to get the theme list
|
||||
const themes = await invoke('get_theme_list') as BackendThemeList[]
|
||||
const list: ThemeList[] = []
|
||||
const themes = await invoke('get_theme_list', {
|
||||
dataDir: `${await dataDir()}/cultivation`
|
||||
}) as BackendThemeList[]
|
||||
const list: ThemeList[] = [
|
||||
// ALWAYS include default theme
|
||||
{
|
||||
name: 'default',
|
||||
version: '1.0.0',
|
||||
description: 'Default theme',
|
||||
includes: {
|
||||
css: [],
|
||||
js: []
|
||||
},
|
||||
path: 'default'
|
||||
}
|
||||
]
|
||||
|
||||
themes.forEach(t => {
|
||||
let obj
|
||||
@@ -44,6 +70,59 @@ export async function getThemeList() {
|
||||
return list
|
||||
}
|
||||
|
||||
export async function loadTheme(theme: string) {
|
||||
// Do some invoke to backend to load the theme
|
||||
export async function getTheme(name: string) {
|
||||
const themes = await getThemeList()
|
||||
|
||||
return themes.find(t => t.name === name) || defaultTheme
|
||||
}
|
||||
|
||||
export async function loadTheme(theme: ThemeList, document: Document) {
|
||||
// We are going to dynamically load stylesheets into the document
|
||||
const head = document.head
|
||||
|
||||
// Get all CSS includes
|
||||
const cssIncludes = theme.includes.css
|
||||
const jsIncludes = theme.includes.js
|
||||
|
||||
// Load CSS files
|
||||
cssIncludes.forEach(css => {
|
||||
if (!css) return
|
||||
|
||||
const link = document.createElement('link')
|
||||
|
||||
link.rel = 'stylesheet'
|
||||
link.href = convertFileSrc(theme.path + '/' + css)
|
||||
head.appendChild(link)
|
||||
})
|
||||
|
||||
// Load JS files
|
||||
jsIncludes.forEach(js => {
|
||||
if (!js) return
|
||||
|
||||
const script = document.createElement('script')
|
||||
|
||||
script.src = convertFileSrc(theme.path + '/' + js)
|
||||
head.appendChild(script)
|
||||
})
|
||||
|
||||
// Set custom background
|
||||
if (theme.customBackgroundURL) {
|
||||
document.body.style.backgroundImage = `url('${theme.customBackgroundURL}')`
|
||||
}
|
||||
|
||||
// Set custom background
|
||||
if (theme.customBackgroundPath) {
|
||||
const bgPath = await dataDir() + 'cultivation/grasscutter/theme.png'
|
||||
|
||||
// Save the background to our data dir
|
||||
await invoke('copy_file', {
|
||||
path: theme.path + '/' + theme.customBackgroundPath,
|
||||
new_path: bgPath
|
||||
})
|
||||
|
||||
// Set the background
|
||||
document.body.style.backgroundImage = `url('${bgPath}')`
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user