more groundwork torwards a settings dialog

feature/settings-window
Michael Ochmann 3 years ago
parent c79b0a91b7
commit 11d5983028
  1. 9
      contextAPI.js
  2. 5
      package-lock.json
  3. 1
      package.json
  4. 31
      src/Ation.js
  5. 13
      src/FontManager.js
  6. 51
      src/SettingsManager.js
  7. 1
      src/WindowManager.js
  8. 5
      src/ui/package-lock.json
  9. 8
      src/ui/src/assets/css/_forms.scss
  10. 9
      src/ui/src/assets/css/_noFile.scss
  11. 63
      src/ui/src/assets/css/_settings.scss
  12. 4
      src/ui/src/assets/css/_variables.scss
  13. 10
      src/ui/src/assets/css/ation.scss
  14. 5
      src/ui/src/components/Ation.js
  15. 28
      src/ui/src/components/settings/General.js
  16. 19
      src/ui/src/components/settings/Navbar.js
  17. 30
      src/ui/src/components/settings/Settings.js
  18. 2
      src/ui/src/index.js

@ -23,5 +23,12 @@ contextBridge.exposeInMainWorld("api", {
closeFile : () => ipcRenderer.send("Ation::closeFile"), closeFile : () => ipcRenderer.send("Ation::closeFile"),
clearCache : () => webFrame.clearCache(), clearCache : () => webFrame.clearCache(),
appVersion : async () => await ipcRenderer.invoke("Ation::appVersion") appVersion : async () => await ipcRenderer.invoke("Ation::appVersion"),
fonts : async () => await ipcRenderer.invoke("FontManager::fonts"),
resize : size => ipcRenderer.invoke("SettingsManager::resize", size)
});
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)
}); });

5
package-lock.json generated

@ -1534,6 +1534,11 @@
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true "dev": true
}, },
"font-list": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/font-list/-/font-list-1.4.5.tgz",
"integrity": "sha512-mLi7Sb4iitgk3mJsTZZDbixssHLoJB4/onWGlQOvTSPW8YPBDQNaMTGOdZ92axQI4UNWwPxV5FmCBOEo1Us7lg=="
},
"form-data": { "form-data": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",

@ -15,6 +15,7 @@
"author": "Michael Ochmann <miko@massivedynamic.eu>", "author": "Michael Ochmann <miko@massivedynamic.eu>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"font-list": "^1.4.5",
"marked": "^4.1.0" "marked": "^4.1.0"
}, },
"build": { "build": {

@ -1,18 +1,22 @@
"use strict"; "use strict";
const {app, protocol, dialog, ipcMain, Menu} = require("electron"); const {app, protocol, dialog, ipcMain, Menu, session} = require("electron");
const path = require("path"); const path = require("path");
const fs = require("fs/promises"); const fs = require("fs/promises");
const fsn = require("fs"); const fsn = require("fs");
const AppInfo = require("../package.json"); const AppInfo = require("../package.json");
const WindowManager = require("./WindowManager"); const WindowManager = require("./WindowManager");
const MainMenu = require("./MainMenu"); const FontManager = require("./FontManager");
const {parser} = require("./Parser"); const SettingsManager = require("./SettingsManager");
const MainMenu = require("./MainMenu");
const {parser} = require("./Parser");
app.commandLine.appendSwitch("disable-http-cache"); app.commandLine.appendSwitch("disable-http-cache");
class Ation { class Ation {
windowManager; windowManager;
fontManager;
settingsManager;
mainMenu; mainMenu;
watcher; watcher;
currentFile; currentFile;
@ -20,10 +24,12 @@ class Ation {
constructor() { constructor() {
if (Ation.Instances > 0) if (Ation.Instances > 0)
throw new Error("Only one Instance of Ation possible"); throw new Error("Only one Instance of Ation possible");
this.currentFile = ""; this.currentFile = "";
this.watcher = null; this.watcher = null;
this.windowManager = new WindowManager(this); this.windowManager = new WindowManager(this);
this.mainMenu = new MainMenu(this); this.fontManager = new FontManager();
this.settingsManager = new SettingsManager(this);
this.mainMenu = new MainMenu(this);
app.on("open-file", (_, path) => { app.on("open-file", (_, path) => {
this.fileToOpen = path; this.fileToOpen = path;
@ -42,6 +48,7 @@ class Ation {
if (["png", "jpg", "jpeg", "svg", "bmp", "gif", "tiff"].includes(extension.toLowerCase())) if (["png", "jpg", "jpeg", "svg", "bmp", "gif", "tiff"].includes(extension.toLowerCase()))
callback(uri); callback(uri);
}); });
}); });
} }

@ -0,0 +1,13 @@
"use strict";
const {ipcMain} = require("electron");
const FontList = require("font-list");
class FontManager {
constructor() {
ipcMain.handle("FontManager::fonts", async () => await FontList.getFonts().catch(error => console.log(error)));
}
}
module.exports = FontManager;

@ -0,0 +1,51 @@
"use strict";
const {ipcMain, app} = require("electron");
const fsn = require("fs");
const fs = require("fs/promises")
const path = require("path");
class SettingsManager {
app;
data;
constructor(app) {
this.app = app;
this.data = null;
if (!SettingsManager.CheckFileSystem()) {
if (!fsn.existsSync(SettingsManager.Folder))
fsn.mkdirSync(SettingsManager.Folder);
fsn.writeFileSync(SettingsManager.File, "{}");
}
this.data = JSON.parse(fsn.readFileSync(SettingsManager.File, {encoding : "utf-8"}));
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));
}
get(key, defaultValue = null) {
if (Object.keys(this.data).includes(key))
return this.data[key];
return defaultValue;
}
set(key, value) {
this.data[key] = value;
this.save();
}
save() {
fs.writeFile(SettingsManager.File, JSON.stringify(this.data, null, 4));
}
static CheckFileSystem() {
return fsn.existsSync(SettingsManager.Folder) && fsn.existsSync(SettingsManager.File);
}
}
SettingsManager.Folder = app.getPath("userData");
SettingsManager.File = path.join(SettingsManager.Folder, "config.json");
module.exports = SettingsManager;

@ -37,6 +37,7 @@ class WindowManager {
if (isDevelopment()) { if (isDevelopment()) {
globalShortcut.register("CommandOrControl+I", () => { globalShortcut.register("CommandOrControl+I", () => {
this.mainWindow.toggleDevTools(); this.mainWindow.toggleDevTools();
this.windows.settings.toggleDevTools();
}); });
globalShortcut.register("CommandOrControl+R", () => { globalShortcut.register("CommandOrControl+R", () => {
this.mainWindow.reload(); this.mainWindow.reload();

@ -5727,6 +5727,11 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
}, },
"font-list": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/font-list/-/font-list-1.4.5.tgz",
"integrity": "sha512-mLi7Sb4iitgk3mJsTZZDbixssHLoJB4/onWGlQOvTSPW8YPBDQNaMTGOdZ92axQI4UNWwPxV5FmCBOEo1Us7lg=="
},
"fork-ts-checker-webpack-plugin": { "fork-ts-checker-webpack-plugin": {
"version": "6.5.2", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz",

@ -25,6 +25,14 @@ button {
} }
} }
select {
background: color(sidebarBackground);
color: color(foreground);
border: none;
border-radius: 6px;
padding: 0.2rem 0.5rem;
}
.checkbox { .checkbox {
display: inline-block; display: inline-block;
width: 2vw; width: 2vw;

@ -25,13 +25,4 @@
} }
} }
} }
.titlebar {
position: absolute;
top: 0;
left: 0;
app-region: drag;
height: 30px;
width: 100%;
}
} }

@ -1,22 +1,77 @@
.settings { .settings {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; overflow: hidden;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
&, * {
user-select: none;
-webkit-user-drag: none;
}
.navbar { .navbar {
padding: 1.5rem 0.5rem 0 0.5rem; padding: 1.5rem 0.5rem 0 0.5rem;
background: color(sidebarBackground); background: color(sidebarBackground);
app-region: drag;
button { a {
text-decoration: none;
padding: 0.3rem 0.5rem; padding: 0.3rem 0.5rem;
display: inline-block;
text-align: center;
border: none;
outline: 0 !important;
color: color(fadedForeground);
margin: 0.5rem 0.1rem;
transition: all 0.2s ease-in-out;
border-radius: 5px;
cursor: pointer;
&:first-child {
margin-left: 0;
}
&.active {
color: color(hightlight);
}
&.active, &:hover {
background: color(active);
}
svg { svg {
font-size: 1.3rem; font-size: 1.3rem;
vertical-align: middle;
} }
label { label {
margin-top: 0.2rem; margin-top: 0.2rem;
display: block; display: block;
font-size: 0.8rem; font-size: 0.7rem;
}
}
}
main {
padding: 1rem 2rem;
font-size: 0.8rem;
.grid {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 0.8rem;
row-gap: 0.5rem;
align-items: center;
input, select {
max-width: 50%;
outline: 0 !important;
}
> * {
&:nth-child(odd) {
justify-self: flex-end;
}
&:nth-child(even) {
align-self: flex-start;
}
} }
} }
} }

@ -2,10 +2,12 @@ $colors : (
//background : #272822, //background : #272822,
background : #1a1a1a, background : #1a1a1a,
foreground : #ddd, foreground : #ddd,
fadedForeground : #888,
sidebarBackground : #333, sidebarBackground : #333,
mainBackground : #222, mainBackground : #222,
scrollbar : rgba(255,255,255,0.2), scrollbar : rgba(255,255,255,0.2),
hightlight : rgb(230,193,123) hightlight : rgb(230,193,123),
active : rgba(255,255,255,0.1)
); );
:root { :root {

@ -27,4 +27,14 @@ body {
background: color(background); background: color(background);
color: color(foreground); color: color(foreground);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
overflow: hidden;
}
.titlebar {
position: absolute;
top: 0;
left: 0;
app-region: drag;
height: 30px;
width: 100%;
} }

@ -12,6 +12,7 @@ import Tips from "./Tips";
import SlideContext from "../shared/SlideContext"; import SlideContext from "../shared/SlideContext";
const Ation = () => { const Ation = () => {
const [font, setFont] = useState("");
const [mode, setMode] = useState(Mode.NORMAL); const [mode, setMode] = useState(Mode.NORMAL);
const [deck, setDeck] = useState([]); const [deck, setDeck] = useState([]);
const [slide, setSlide] = useState(0); const [slide, setSlide] = useState(0);
@ -36,6 +37,8 @@ const Ation = () => {
setDeck([]); setDeck([]);
}); });
(async set => set(await window.api.appVersion()))(setVersion); (async set => set(await window.api.appVersion()))(setVersion);
(async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont);
}, [basePath, slide]); }, [basePath, slide]);
const openFile = () => { const openFile = () => {
@ -52,7 +55,7 @@ const Ation = () => {
<Toolbar openFile={openFile} setShowTips={setShowTips} version={version} /> <Toolbar openFile={openFile} setShowTips={setShowTips} version={version} />
<SlidesList deck={deck} /> <SlidesList deck={deck} />
<main className="main"> <main className="main">
<Slide data={deck[slide] || null} /> <Slide data={deck[slide] || null} style={{fontFamily : font}}/>
</main> </main>
<Tips show={showTips} /> <Tips show={showTips} />
</section> </section>

@ -0,0 +1,28 @@
import React, {useState, useEffect} 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);
}, []);
return (
<section className="grid">
<label>Font in slides:</label>
<select value={font} onChange={changeFont}>
{fonts.map(font => font.trim().replace(/(^"|"$)/g, "")).map(font => <option key={font} value={font}>{font}</option>)}
</select>
<label>Global highlight color:</label>
<input type="color" />
</section>
);
};
export default General;

@ -1,16 +1,21 @@
import React from "react"; import React, {forwardRef} from "react";
import {Gear} from "react-bootstrap-icons"; import {NavLink} from "react-router-dom";
import {Gear} from "react-bootstrap-icons";
const Navbar = () => { const Navbar = forwardRef((props, ref) => {
return ( return (
<nav className="navbar"> <nav className="navbar" ref={ref}>
<button> <NavLink to="/settings" end>
<Gear /> <Gear />
<label>General</label> <label>General</label>
</button> </NavLink>
<NavLink to="/settings/style" end>
<Gear />
<label>General</label>
</NavLink>
</nav> </nav>
); );
}; });
export default Navbar; export default Navbar;

@ -1,11 +1,35 @@
import React from "react"; import React, {useRef, useState, useEffect} from "react";
import {Routes, Route, useLocation} from "react-router-dom";
import Navbar from "./Navbar"; import Navbar from "./Navbar";
import General from "./General";
const Settings = () => { const Settings = () => {
const navbar = useRef();
const content = useRef()
const location = useLocation();
const [fonts, setFonts] = useState([]);
useEffect(() => {
(async setFonts => setFonts(await window.api.fonts()))(setFonts);
}, []);
useEffect(() => {
if (!navbar.current || !content.current)
return;
const newHeight = navbar.current.clientHeight + content.current.clientHeight;
window.api.resize(newHeight + 10);
}, [location, navbar, content]);
return ( return (
<section className="settings"> <section className="settings">
<Navbar /> <Navbar ref={navbar} />
<main ref={content}>
<Routes>
<Route index element={<General fonts={fonts} />} />
</Routes>
</main>
<section className="titlebar"></section>
</section> </section>
); );
}; };

@ -17,7 +17,7 @@ root.render(
<Router> <Router>
<Routes> <Routes>
<Route index element={<App />} /> <Route index element={<App />} />
<Route path="/settings" element={<Settings />} /> <Route path="/settings/*" element={<Settings />} />
</Routes> </Routes>
</Router> </Router>
</React.StrictMode> </React.StrictMode>

Loading…
Cancel
Save