mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-14 16:14:48 +01:00
extras menu for conditional extras
This commit is contained in:
11
src/resources/icons/plus.svg
Normal file
11
src/resources/icons/plus.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
<desc>Created with Fabric.js 1.7.22</desc>
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(128 128) scale(0.72 0.72)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05) scale(3.89 3.89)" >
|
||||||
|
<path d="M 58.921 90 H 31.079 c -1.155 0 -2.092 -0.936 -2.092 -2.092 V 2.092 C 28.988 0.936 29.924 0 31.079 0 h 27.841 c 1.155 0 2.092 0.936 2.092 2.092 v 85.817 C 61.012 89.064 60.076 90 58.921 90 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 90 31.079 v 27.841 c 0 1.155 -0.936 2.092 -2.092 2.092 H 2.092 C 0.936 61.012 0 60.076 0 58.921 V 31.079 c 0 -1.155 0.936 -2.092 2.092 -2.092 h 85.817 C 89.064 28.988 90 29.924 90 31.079 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -114,6 +114,9 @@ select:focus {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ExtrasMenu {
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-height: 580px) {
|
@media (max-height: 580px) {
|
||||||
.BottomSection {
|
.BottomSection {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import DownloadHandler from '../utils/download'
|
|||||||
import cogBtn from '../resources/icons/cog.svg'
|
import cogBtn from '../resources/icons/cog.svg'
|
||||||
import downBtn from '../resources/icons/download.svg'
|
import downBtn from '../resources/icons/download.svg'
|
||||||
import wrenchBtn from '../resources/icons/wrench.svg'
|
import wrenchBtn from '../resources/icons/wrench.svg'
|
||||||
|
import Menu from './components/menu/Menu'
|
||||||
|
import { ExtrasMenu } from './components/menu/ExtrasMenu'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadHandler: DownloadHandler
|
downloadHandler: DownloadHandler
|
||||||
@@ -35,7 +37,9 @@ interface IState {
|
|||||||
miniDownloadsOpen: boolean
|
miniDownloadsOpen: boolean
|
||||||
downloadsOpen: boolean
|
downloadsOpen: boolean
|
||||||
gameDownloadsOpen: boolean
|
gameDownloadsOpen: boolean
|
||||||
|
extrasOpen: boolean
|
||||||
migotoSet: boolean
|
migotoSet: boolean
|
||||||
|
playGame: (exe?: string, proc_name?: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Main extends React.Component<IProps, IState> {
|
export class Main extends React.Component<IProps, IState> {
|
||||||
@@ -47,7 +51,11 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
miniDownloadsOpen: false,
|
miniDownloadsOpen: false,
|
||||||
downloadsOpen: false,
|
downloadsOpen: false,
|
||||||
gameDownloadsOpen: false,
|
gameDownloadsOpen: false,
|
||||||
|
extrasOpen: false,
|
||||||
migotoSet: false,
|
migotoSet: false,
|
||||||
|
playGame: () => {
|
||||||
|
alert('Error launching game')
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
listen('lang_error', (payload) => {
|
listen('lang_error', (payload) => {
|
||||||
@@ -87,6 +95,8 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
min = false
|
min = false
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
|
this.openExtrasMenu = this.openExtrasMenu.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -113,6 +123,13 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openExtrasMenu(playGame: () => void) {
|
||||||
|
this.setState({
|
||||||
|
extrasOpen: true,
|
||||||
|
playGame,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -153,6 +170,15 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
<NewsSection />
|
<NewsSection />
|
||||||
|
|
||||||
|
{
|
||||||
|
// Extras section
|
||||||
|
this.state.extrasOpen && (
|
||||||
|
<ExtrasMenu closeFn={() => this.setState({ extrasOpen: false })} playGame={this.state.playGame}>
|
||||||
|
Yo
|
||||||
|
</ExtrasMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Mini downloads section
|
// Mini downloads section
|
||||||
this.state.miniDownloadsOpen ? (
|
this.state.miniDownloadsOpen ? (
|
||||||
@@ -201,7 +227,7 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div className="BottomSection" id="bottomSectionContainer">
|
<div className="BottomSection" id="bottomSectionContainer">
|
||||||
<ServerLaunchSection />
|
<ServerLaunchSection openExtras={this.openExtrasMenu} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="DownloadProgress"
|
id="DownloadProgress"
|
||||||
|
|||||||
@@ -82,7 +82,12 @@
|
|||||||
width: 5%;
|
width: 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.AkebiIcon,
|
#ExtrasMenuButton {
|
||||||
|
width: 5%;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtrasIcon,
|
||||||
.ServerIcon {
|
.ServerIcon {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
filter: invert(28%) sepia(28%) saturate(1141%) hue-rotate(352deg) brightness(96%) contrast(88%);
|
filter: invert(28%) sepia(28%) saturate(1141%) hue-rotate(352deg) brightness(96%) contrast(88%);
|
||||||
|
|||||||
@@ -8,13 +8,17 @@ import { translate } from '../../utils/language'
|
|||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
import Server from '../../resources/icons/server.svg'
|
import Server from '../../resources/icons/server.svg'
|
||||||
import Akebi from '../../resources/icons/akebi.svg'
|
import Plus from '../../resources/icons/plus.svg'
|
||||||
|
|
||||||
import './ServerLaunchSection.css'
|
import './ServerLaunchSection.css'
|
||||||
import { dataDir } from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import { getGameExecutable } from '../../utils/game'
|
import { getGameExecutable } from '../../utils/game'
|
||||||
import { patchGame, unpatchGame } from '../../utils/metadata'
|
import { patchGame, unpatchGame } from '../../utils/metadata'
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
openExtras: (playGame: () => void) => void
|
||||||
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
grasscutterEnabled: boolean
|
grasscutterEnabled: boolean
|
||||||
buttonLabel: string
|
buttonLabel: string
|
||||||
@@ -35,8 +39,8 @@ interface IState {
|
|||||||
migotoSet: boolean
|
migotoSet: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ServerLaunchSection extends React.Component<{}, IState> {
|
export default class ServerLaunchSection extends React.Component<IProps, IState> {
|
||||||
constructor(props: {}) {
|
constructor(props: IProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -57,8 +61,6 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
||||||
this.playGame = this.playGame.bind(this)
|
this.playGame = this.playGame.bind(this)
|
||||||
this.launchAkebi = this.launchAkebi.bind(this)
|
|
||||||
this.launchMigoto = this.launchMigoto.bind(this)
|
|
||||||
this.setIp = this.setIp.bind(this)
|
this.setIp = this.setIp.bind(this)
|
||||||
this.setPort = this.setPort.bind(this)
|
this.setPort = this.setPort.bind(this)
|
||||||
this.toggleHttps = this.toggleHttps.bind(this)
|
this.toggleHttps = this.toggleHttps.bind(this)
|
||||||
@@ -189,30 +191,6 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchAkebi() {
|
|
||||||
const config = await getConfig()
|
|
||||||
|
|
||||||
// Get game exe from game path, so we can watch it
|
|
||||||
const pathArr = config.game_install_path.replace(/\\/g, '/').split('/')
|
|
||||||
const gameExec = pathArr[pathArr.length - 1]
|
|
||||||
|
|
||||||
await this.playGame(config.akebi_path, gameExec)
|
|
||||||
}
|
|
||||||
|
|
||||||
async launchMigoto() {
|
|
||||||
const config = await getConfig()
|
|
||||||
|
|
||||||
if (!config.migoto_path) return alert('Migoto not installed or set!')
|
|
||||||
|
|
||||||
// Get game exe from game path, so we can watch it
|
|
||||||
const pathArr = config.migoto_path.replace(/\\/g, '/').split('/')
|
|
||||||
const migotoExec = pathArr[pathArr.length - 1]
|
|
||||||
|
|
||||||
await invoke('run_program_relative', { path: config.migoto_path })
|
|
||||||
|
|
||||||
await this.playGame()
|
|
||||||
}
|
|
||||||
|
|
||||||
setIp(text: string) {
|
setIp(text: string) {
|
||||||
this.setState({
|
this.setState({
|
||||||
ip: text,
|
ip: text,
|
||||||
@@ -286,19 +264,9 @@ export default class ServerLaunchSection extends React.Component<{}, IState> {
|
|||||||
{this.state.buttonLabel}
|
{this.state.buttonLabel}
|
||||||
</BigButton>
|
</BigButton>
|
||||||
{this.state.swag && (
|
{this.state.swag && (
|
||||||
<>
|
<BigButton onClick={() => this.props.openExtras(this.playGame)} id="ExtrasMenuButton">
|
||||||
{this.state.akebiSet && (
|
<img className="ExtrasIcon" id="extrasIcon" src={Plus} />
|
||||||
<BigButton onClick={this.launchAkebi} id="akebiLaunch">
|
</BigButton>
|
||||||
<img className="AkebiIcon" id="akebiIcon" src={Akebi} />
|
|
||||||
</BigButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{this.state.migotoSet && (
|
|
||||||
<BigButton onClick={this.launchMigoto} id="migotoLaunch">
|
|
||||||
3DM
|
|
||||||
</BigButton>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<BigButton onClick={this.launchServer} id="serverLaunch">
|
<BigButton onClick={this.launchServer} id="serverLaunch">
|
||||||
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
|
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
|
||||||
|
|||||||
32
src/ui/components/menu/ExtrasMenu.css
Normal file
32
src/ui/components/menu/ExtrasMenu.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.ExtrasMenu {
|
||||||
|
width: 20%;
|
||||||
|
height: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtrasMenu .MenuInner {
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtrasMenuContent {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtraItem {
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExtraLaunch .BigButton {
|
||||||
|
padding: 20px 50px;
|
||||||
|
}
|
||||||
123
src/ui/components/menu/ExtrasMenu.tsx
Normal file
123
src/ui/components/menu/ExtrasMenu.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { getConfig } from '../../../utils/configuration'
|
||||||
|
import Checkbox from '../common/Checkbox'
|
||||||
|
import Menu from './Menu'
|
||||||
|
|
||||||
|
import './ExtrasMenu.css'
|
||||||
|
import BigButton from '../common/BigButton'
|
||||||
|
import { invoke } from '@tauri-apps/api'
|
||||||
|
import Tr from '../../../utils/language'
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children: React.ReactNode | React.ReactNode[]
|
||||||
|
closeFn: () => void
|
||||||
|
playGame: (exe?: string, proc_name?: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
migoto?: string
|
||||||
|
akebi?: string
|
||||||
|
launch_migoto: boolean
|
||||||
|
launch_akebi: boolean
|
||||||
|
reshade?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtrasMenu extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
launch_migoto: false,
|
||||||
|
launch_akebi: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.launchPreprograms = this.launchPreprograms.bind(this)
|
||||||
|
this.toggleMigoto = this.toggleMigoto.bind(this)
|
||||||
|
this.toggleAkebi = this.toggleAkebi.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
const config = await getConfig()
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
migoto: config.migoto_path,
|
||||||
|
akebi: config.akebi_path,
|
||||||
|
// TODO reshade
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async launchPreprograms() {
|
||||||
|
// This injects independent of the game
|
||||||
|
if (this.state.launch_migoto) {
|
||||||
|
await this.launchMigoto()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will launch the game
|
||||||
|
if (this.state.launch_akebi) {
|
||||||
|
await this.launchAkebi()
|
||||||
|
|
||||||
|
// This already launches the game
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch the game
|
||||||
|
await this.props.playGame()
|
||||||
|
}
|
||||||
|
|
||||||
|
async launchAkebi() {
|
||||||
|
const config = await getConfig()
|
||||||
|
|
||||||
|
// Get game exe from game path, so we can watch it
|
||||||
|
const pathArr = config.game_install_path.replace(/\\/g, '/').split('/')
|
||||||
|
const gameExec = pathArr[pathArr.length - 1]
|
||||||
|
|
||||||
|
await this.props.playGame(config.akebi_path, gameExec)
|
||||||
|
}
|
||||||
|
|
||||||
|
async launchMigoto() {
|
||||||
|
const config = await getConfig()
|
||||||
|
|
||||||
|
if (!config.migoto_path) return alert('Migoto not installed or set!')
|
||||||
|
|
||||||
|
await invoke('run_program_relative', { path: config.migoto_path })
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMigoto() {
|
||||||
|
this.setState({
|
||||||
|
launch_migoto: !this.state.launch_migoto,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAkebi() {
|
||||||
|
this.setState({
|
||||||
|
launch_akebi: !this.state.launch_akebi,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Menu closeFn={this.props.closeFn} heading="Extras" className="ExtrasMenu">
|
||||||
|
<div className="ExtrasMenuContent">
|
||||||
|
{this.state.migoto && (
|
||||||
|
<div className="ExtraItem">
|
||||||
|
<div className="ExtraItemLabel">Migoto</div>
|
||||||
|
<Checkbox id="MigotoCheckbox" checked={this.state.launch_migoto} onChange={this.toggleMigoto} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{this.state.akebi && (
|
||||||
|
<div className="ExtraItem">
|
||||||
|
<div className="ExtraItemLabel">Akebi</div>
|
||||||
|
<Checkbox id="AkebiCheckbox" checked={this.state.launch_akebi} onChange={this.toggleAkebi} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="ExtraLaunch">
|
||||||
|
<BigButton id="ExtraLaunch" onClick={this.launchPreprograms}>
|
||||||
|
<Tr text="main.launch_button" />
|
||||||
|
</BigButton>
|
||||||
|
</div>
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,11 +102,7 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
) : (
|
) : (
|
||||||
<div className="ModTileOpen">
|
<div className="ModTileOpen">
|
||||||
<img src={Folder} className="ModTileFolder" alt="Open" onClick={this.openInExplorer} />
|
<img src={Folder} className="ModTileFolder" alt="Open" onClick={this.openInExplorer} />
|
||||||
<Checkbox
|
<Checkbox checked={this.state.modEnabled} id={this.props.mod.name} onChange={this.toggleMod} />
|
||||||
checked={/* TODO GET ACTUAL INSTALL STATUS */ this.state.modEnabled}
|
|
||||||
id={this.props.mod.name}
|
|
||||||
onChange={this.toggleMod}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<img
|
<img
|
||||||
|
|||||||
Reference in New Issue
Block a user