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"),
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==",
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",

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

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

@ -5727,6 +5727,11 @@
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"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": {
"version": "6.5.2",
"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 {
display: inline-block;
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 {
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 {
padding: 1.5rem 0.5rem 0 0.5rem;
background: color(sidebarBackground);
app-region: drag;
button {
a {
text-decoration: none;
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 {
font-size: 1.3rem;
vertical-align: middle;
}
label {
margin-top: 0.2rem;
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 : #1a1a1a,
foreground : #ddd,
fadedForeground : #888,
sidebarBackground : #333,
mainBackground : #222,
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 {

@ -27,4 +27,14 @@ body {
background: color(background);
color: color(foreground);
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";
const Ation = () => {
const [font, setFont] = useState("");
const [mode, setMode] = useState(Mode.NORMAL);
const [deck, setDeck] = useState([]);
const [slide, setSlide] = useState(0);
@ -36,6 +37,8 @@ const Ation = () => {
setDeck([]);
});
(async set => set(await window.api.appVersion()))(setVersion);
(async setFont => setFont(await window.appSettings.get("font", "Iosevka")))(setFont);
}, [basePath, slide]);
const openFile = () => {
@ -52,7 +55,7 @@ const Ation = () => {
<Toolbar openFile={openFile} setShowTips={setShowTips} version={version} />
<SlidesList deck={deck} />
<main className="main">
<Slide data={deck[slide] || null} />
<Slide data={deck[slide] || null} style={{fontFamily : font}}/>
</main>
<Tips show={showTips} />
</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 (
<nav className="navbar">
<button>
<nav className="navbar" ref={ref}>
<NavLink to="/settings" end>
<Gear />
<label>General</label>
</button>
</NavLink>
<NavLink to="/settings/style" end>
<Gear />
<label>General</label>
</NavLink>
</nav>
);
};
});
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 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 (
<section className="settings">
<Navbar />
<Navbar ref={navbar} />
<main ref={content}>
<Routes>
<Route index element={<General fonts={fonts} />} />
</Routes>
</main>
<section className="titlebar"></section>
</section>
);
};

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

Loading…
Cancel
Save