From 6bf88714fa436c6835c77b1a5e305bc18d2e55c3 Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Fri, 7 Oct 2022 00:18:15 +0200 Subject: [PATCH] finished basic implementation of settings --- contextAPI.js | 16 ++++++--- package.json | 2 +- src/Ation.js | 10 +++--- src/MainMenu.js | 28 +++++++++++---- src/SettingsManager.js | 6 ++++ src/WindowManager.js | 12 +++++-- src/ui/src/assets/css/_variables.scss | 2 +- src/ui/src/components/Ation.js | 11 +++--- src/ui/src/components/settings/Appearance.js | 36 +++++++++++++++++++ src/ui/src/components/settings/General.js | 25 +++---------- src/ui/src/components/settings/Navbar.js | 10 +++--- src/ui/src/components/settings/Settings.js | 10 +++--- .../src/higherOrderComponents/withSettings.js | 22 ++++++++++++ src/ui/src/index.js | 9 ++--- src/ui/src/shared/SettingsContext.js | 5 +++ 15 files changed, 146 insertions(+), 58 deletions(-) create mode 100644 src/ui/src/components/settings/Appearance.js create mode 100644 src/ui/src/higherOrderComponents/withSettings.js create mode 100644 src/ui/src/shared/SettingsContext.js diff --git a/contextAPI.js b/contextAPI.js index 835963d..8480f31 100644 --- a/contextAPI.js +++ b/contextAPI.js @@ -2,8 +2,9 @@ const {contextBridge, ipcRenderer, webFrame} = require("electron"); -let fileOpenListener = null; -let closeFileListener = null; +let fileOpenListener = null; +let closeFileListener = null; +let settingsChangeListener = null; contextBridge.exposeInMainWorld("api", { openFileDialog : () => ipcRenderer.send("WindowManager::openFileDialog"), @@ -29,6 +30,13 @@ contextBridge.exposeInMainWorld("api", { }); contextBridge.exposeInMainWorld("appSettings", { - get : async (key, defaultValue = null) => await ipcRenderer.invoke("SettingsManager::get", key, defaultValue), - set : async (key, value) => await ipcRenderer.invoke("SettingsManager::set", key, value) + get : async (key, defaultValue = null) => await ipcRenderer.invoke("SettingsManager::get", key, defaultValue), + set : async (key, value) => await ipcRenderer.invoke("SettingsManager::set", key, value), + all : async () => await ipcRenderer.invoke("SettingsManager::all"), + onChange : callback => { + if (settingsChangeListener) + ipcRenderer.off("SettingsManager::change", settingsChangeListener); + settingsChangeListener = (_, settings) => callback(settings); + ipcRenderer.on("SettingsManager::change", settingsChangeListener); + } }); \ No newline at end of file diff --git a/package.json b/package.json index 0fe6f60..e517989 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ation", - "version": "0.2.0", + "version": "0.3.0", "description": "a simple presentation software", "main": "main.js", "scripts": { diff --git a/src/Ation.js b/src/Ation.js index 6cdf3f7..1ab25dc 100644 --- a/src/Ation.js +++ b/src/Ation.js @@ -1,8 +1,8 @@ "use strict"; -const {app, protocol, dialog, ipcMain, Menu, session} = require("electron"); -const path = require("path"); -const fs = require("fs/promises"); -const fsn = require("fs"); +const {app, protocol, dialog, ipcMain, Menu} = require("electron"); +const path = require("path"); +const fs = require("fs/promises"); +const fsn = require("fs"); const AppInfo = require("../package.json"); const WindowManager = require("./WindowManager"); @@ -39,6 +39,8 @@ class Ation { ipcMain.on("Ation::closeFile", () => this.closeFile()); app.whenReady().then(async () => { + this.settingsManager.change(); + if (this.fileToOpen) this.openFile(this.fileToOpen); protocol.registerFileProtocol("slideimg", (request, callback) => { diff --git a/src/MainMenu.js b/src/MainMenu.js index e1beed8..9224d93 100644 --- a/src/MainMenu.js +++ b/src/MainMenu.js @@ -1,19 +1,35 @@ "use strict"; -const {Menu} = require("electron"); +const {Menu, app} = require("electron"); class MainMenu { - constructor(app) { - this.app = app; + constructor(parentApp) { + this.app = parentApp; this.menu = null; this.buildItems(); Menu.setApplicationMenu(this.menu); } buildItems() { - const template = [ + const settingsItem = { + label : "Settings", + click : () => this.app.windowManager.windows.settings.show() + }; + const template = [ ...(process.platform === "darwin" ? [{ - role : "appMenu" + label: app.name, + submenu: [ + { role: "about" }, + {...settingsItem}, + { type: "separator" }, + { role: "services" }, + { type: "separator" }, + { role: "hide" }, + { role: "hideOthers" }, + { role: "unhide" }, + { type: "separator" }, + { role: "quit" } + ] }] : []), { label : "File", @@ -29,7 +45,7 @@ class MainMenu { accelerator : "CommandOrControl+W", click : () => this.app.closeFile(), enabled : this.app.currentFile !== "" - } + }, ...(process.platform !== "darwin" ? [{type : "separator"}, {...settingsItem}] : []) ] }, { diff --git a/src/SettingsManager.js b/src/SettingsManager.js index 1edd18c..a427178 100644 --- a/src/SettingsManager.js +++ b/src/SettingsManager.js @@ -23,6 +23,7 @@ class SettingsManager { ipcMain.handle("SettingsManager::resize", (_, height) => app.windowManager.windows.settings.setSize(800, height, true)); ipcMain.handle("SettingsManager::get", (_, key, defaultValue = null) => this.get(key, defaultValue)); ipcMain.handle("SettingsManager::set", (_, key, value) => this.set(key, value)); + ipcMain.handle("SettingsManager::all", () => this.data); } get(key, defaultValue = null) { @@ -36,8 +37,13 @@ class SettingsManager { this.save(); } + change() { + this.app.windowManager.mainWindow.send("SettingsManager::change", this.data); + } + save() { fs.writeFile(SettingsManager.File, JSON.stringify(this.data, null, 4)); + this.change(); } static CheckFileSystem() { diff --git a/src/WindowManager.js b/src/WindowManager.js index b28f6d9..e4d95c7 100644 --- a/src/WindowManager.js +++ b/src/WindowManager.js @@ -31,7 +31,12 @@ class WindowManager { resizable : false, fullscreenable : false, parent : this.mainWindow, - show : true + show : false + }, this.mainWindow, false); + + this.windows.settings.on("close", event => { + event.preventDefault(); + this.windows.settings.hide(); }); if (isDevelopment()) { @@ -54,7 +59,7 @@ class WindowManager { } } - static _CreateWindow(options = null, parent = null) { + static _CreateWindow(options = null, parent = null, show = true) { const windowOptions = { width : 800, height : 600, @@ -71,7 +76,8 @@ class WindowManager { if (!parent) delete windowOptions.parent; const window = new BrowserWindow(windowOptions); - window.once("ready-to-show", () => window.show()); + if (show) + window.once("ready-to-show", () => window.show()); return window; } diff --git a/src/ui/src/assets/css/_variables.scss b/src/ui/src/assets/css/_variables.scss index 4bbc8b0..a9a9b14 100644 --- a/src/ui/src/assets/css/_variables.scss +++ b/src/ui/src/assets/css/_variables.scss @@ -6,7 +6,7 @@ $colors : ( sidebarBackground : #333, mainBackground : #222, scrollbar : rgba(255,255,255,0.2), - hightlight : rgb(230,193,123), + hightlight : #e6c17b, active : rgba(255,255,255,0.1) ); diff --git a/src/ui/src/components/Ation.js b/src/ui/src/components/Ation.js index 3b60e5b..4871964 100644 --- a/src/ui/src/components/Ation.js +++ b/src/ui/src/components/Ation.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from "react"; +import React, {useEffect, useState, useContext} from "react"; import SlidesList from "./SlidesList"; import Mode from "../models/Mode"; @@ -9,10 +9,11 @@ import NoFile from "./NoFile"; import Toolbar from "./Toolbar"; import Tips from "./Tips"; -import SlideContext from "../shared/SlideContext"; +import SlideContext from "../shared/SlideContext"; +import SettingsContext from "../shared/SettingsContext"; const Ation = () => { - const [font, setFont] = useState(""); + const {font, highlightColor} = useContext(SettingsContext); const [mode, setMode] = useState(Mode.NORMAL); const [deck, setDeck] = useState([]); const [slide, setSlide] = useState(0); @@ -37,7 +38,7 @@ const Ation = () => { setDeck([]); }); (async set => set(await window.api.appVersion()))(setVersion); - (async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont); + //(async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont); }, [basePath, slide]); @@ -54,7 +55,7 @@ const Ation = () => {
-
+
diff --git a/src/ui/src/components/settings/Appearance.js b/src/ui/src/components/settings/Appearance.js new file mode 100644 index 0000000..611bc42 --- /dev/null +++ b/src/ui/src/components/settings/Appearance.js @@ -0,0 +1,36 @@ +import React, {useState, useEffect} from "react"; + +const Appearance = ({fonts}) => { + const [font, setFont] = useState(""); + const [highlightColor, setHighlightColor] = useState(""); + + const changeFont = event => { + const value = event.target.value; + setFont(value); + window.appSettings.set("font", value); + }; + + const changeColor = event => { + const value = event.target.value; + setHighlightColor(value); + window.appSettings.set("highlightColor", value); + }; + + useEffect(() => { + (async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont); + (async setHighlightColor => setHighlightColor(await window.appSettings.get("highlightColor", "#e6c17b")))(setHighlightColor); + }, []); + + return ( +
+ + + + +
+ ); +}; + +export default Appearance; \ No newline at end of file diff --git a/src/ui/src/components/settings/General.js b/src/ui/src/components/settings/General.js index a4d2101..3f18c80 100644 --- a/src/ui/src/components/settings/General.js +++ b/src/ui/src/components/settings/General.js @@ -1,27 +1,10 @@ -import React, {useState, useEffect} from "react"; +import React from "react"; -const General = ({fonts}) => { - const [font, setFont] = useState(""); - - const changeFont = event => { - const value = event.target.value; - setFont(value); - window.appSettings.set("font", value); - }; - - useEffect(() => { - (async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont); - }, []); +const General = () => { return ( -
- - - - -
+ <> + ); }; diff --git a/src/ui/src/components/settings/Navbar.js b/src/ui/src/components/settings/Navbar.js index c0e3519..8c46aa6 100644 --- a/src/ui/src/components/settings/Navbar.js +++ b/src/ui/src/components/settings/Navbar.js @@ -1,9 +1,9 @@ import React, {forwardRef} from "react"; -import {NavLink} from "react-router-dom"; -import {Gear} from "react-bootstrap-icons"; +import {NavLink} from "react-router-dom"; +import {Gear, Palette} from "react-bootstrap-icons"; -const Navbar = forwardRef((props, ref) => { +const Navbar = forwardRef((_, ref) => { return ( ); diff --git a/src/ui/src/components/settings/Settings.js b/src/ui/src/components/settings/Settings.js index 7269d5d..b1a6923 100644 --- a/src/ui/src/components/settings/Settings.js +++ b/src/ui/src/components/settings/Settings.js @@ -1,8 +1,9 @@ import React, {useRef, useState, useEffect} from "react"; -import {Routes, Route, useLocation} from "react-router-dom"; +import {Routes, Route, useLocation} from "react-router-dom"; -import Navbar from "./Navbar"; -import General from "./General"; +import Navbar from "./Navbar"; +import General from "./General"; +import Appearance from "./Appearance"; const Settings = () => { const navbar = useRef(); @@ -26,7 +27,8 @@ const Settings = () => {
- } /> + } /> + } />
diff --git a/src/ui/src/higherOrderComponents/withSettings.js b/src/ui/src/higherOrderComponents/withSettings.js new file mode 100644 index 0000000..f063cf1 --- /dev/null +++ b/src/ui/src/higherOrderComponents/withSettings.js @@ -0,0 +1,22 @@ +import React, {useState, useEffect} from "react"; + +import SettingsContext from "../shared/SettingsContext"; + +const withSettings = Component => () => { + const [settings, setSettings] = useState({ + font : "Iosevka" + }); + + useEffect(() => { + (async setSettings => setSettings(await window.appSettings.all()))(setSettings); + window.appSettings.onChange(settings => setSettings(settings)); + }, []); + + return ( + + + + ); +}; + +export default withSettings; \ No newline at end of file diff --git a/src/ui/src/index.js b/src/ui/src/index.js index e6c207e..3bf93c9 100644 --- a/src/ui/src/index.js +++ b/src/ui/src/index.js @@ -3,13 +3,14 @@ import ReactDOM from "react-dom/client"; import {BrowserRouter as Router, Routes, Route} from "react-router-dom"; -import Ation from "./components/Ation"; -import Settings from "./components/settings/Settings"; -import withDrop from "./higherOrderComponents/withDrop"; +import Ation from "./components/Ation"; +import Settings from "./components/settings/Settings"; +import withDrop from "./higherOrderComponents/withDrop"; +import withSettings from "./higherOrderComponents/withSettings"; import "./assets/css/ation.scss"; -const App = withDrop(Ation); +const App = withSettings(withDrop(Ation)); const root = ReactDOM.createRoot(document.getElementById("root")); root.render( diff --git a/src/ui/src/shared/SettingsContext.js b/src/ui/src/shared/SettingsContext.js new file mode 100644 index 0000000..feb3e4b --- /dev/null +++ b/src/ui/src/shared/SettingsContext.js @@ -0,0 +1,5 @@ +import {createContext} from "react"; + +const SettingsContext = createContext({}); + +export default SettingsContext; \ No newline at end of file