Skip to content

Commit

Permalink
[desktop] make fonts depend on language and platform, close #1909
Browse files Browse the repository at this point in the history
adds a function getFonts() to main-styles.js that returns the
font-family string for the body style.

will add additional fonts depending on the platform and used language.

AppearanceSettingsViewer will now also cause a regeneration of the
style tags and a change of language on the native side when the language
is changed by the user.
  • Loading branch information
ganthern committed May 12, 2020
1 parent 32ccb68 commit 46cc389
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 86 deletions.
3 changes: 3 additions & 0 deletions app-android/app/src/main/java/de/tutao/tutanota/Native.java
Expand Up @@ -266,6 +266,9 @@ private Promise<Object, Exception, Void> invokeMethod(String method, JSONArray a
// new AlarmNotificationsManager(new AndroidKeyStoreFacade(activity), sseStorage, new Crypto(activity), new SystemAlarmFacade(activity))
// .unscheduleAlarms(args.getString(0));
// return Utils.resolvedDeferred(null);
case "changeLanguage":
promise.resolve(null);
break;
default:
throw new Exception("unsupported method: " + method);
}
Expand Down
4 changes: 3 additions & 1 deletion app-ios/tutanota/Sources/TUTViewController.m
Expand Up @@ -183,6 +183,8 @@ - (void)userContentController:(nonnull WKUserContentController *)userContentCont
[_fileChooser openWithAnchorRect:rect completion: sendResponseBlock];
} else if ([@"getName" isEqualToString:type]) {
[_fileUtil getNameForPath:arguments[0] completion:sendResponseBlock];
} else if ([@"changeLanguage" isEqualToString:type]) {
sendResponseBlock(NSNull.null, nil);
} else if ([@"getSize" isEqualToString:type]) {
[_fileUtil getSizeForPath:arguments[0] completion:sendResponseBlock];
} else if ([@"getMimeType" isEqualToString:type]) {
Expand Down Expand Up @@ -482,7 +484,7 @@ - (UIStatusBarStyle)preferredStatusBarStyle {
} else {
// Since iOS 13 UIStatusBarStyleDefault respects dark mode and we just want dark text
if (@available(iOS 13, *)) {
return UIStatusBarStyleDarkContent;
return UIStatusBarStyleLightContent;
} else {
return UIStatusBarStyleDefault;
}
Expand Down
8 changes: 7 additions & 1 deletion dist.js
Expand Up @@ -316,7 +316,13 @@ function createHtml(env) {
function createLanguageBundles(bundles) {
const languageFiles = options.stage === 'release' || options.stage === 'prod'
? glob.sync('src/translations/*.js')
: ['src/translations/en.js', 'src/translations/de.js', 'src/translations/de_sie.js', 'src/translations/ru.js']
: [
'src/translations/en.js',
'src/translations/de.js',
'src/translations/de_sie.js',
'src/translations/ru.js',
'src/translations/ja.js'
]
return Promise.all(languageFiles.map(translation => {
let filename = path.basename(translation)
return builder.bundle(translation, {
Expand Down
1 change: 1 addition & 0 deletions flow/api/types.js
Expand Up @@ -198,6 +198,7 @@ type NativeRequestType = 'init'
| 'setSearchOverlayState'
| 'closeApp'
| 'unload' // desktop
| 'changeLanguage'


type JsRequestType = 'createMailEditor'
Expand Down
5 changes: 4 additions & 1 deletion src/app.js
Expand Up @@ -25,6 +25,7 @@ import {styles} from "./gui/styles.js"
import {deviceConfig} from "./misc/DeviceConfig"
import {Logger, replaceNativeLogger} from "./api/common/Logger"
import {ButtonType} from "./gui/base/ButtonN"
import {changeSystemLanguage} from "./native/SystemApp"

assertMainOrNodeBoot()
bootFinished()
Expand Down Expand Up @@ -121,7 +122,9 @@ let initialized = lang.init(en).then(() => {

const userLanguage = deviceConfig.getLanguage() && languages.find((l) => l.code === deviceConfig.getLanguage())
if (userLanguage) {
lang.setLanguage({code: userLanguage.code, languageTag: languageCodeToTag(userLanguage.code)})
const language = {code: userLanguage.code, languageTag: languageCodeToTag(userLanguage.code)}
lang.setLanguage(language)
.then(() => changeSystemLanguage(language))
}

function createViewResolver(getView: lazy<Promise<View>>, requireLogin: boolean = true,
Expand Down
19 changes: 9 additions & 10 deletions src/desktop/ApplicationWindow.js
@@ -1,6 +1,6 @@
// @flow
import type {ElectronPermission, FindInPageResult} from 'electron'
import {BrowserWindow, Menu, shell, WebContents} from 'electron'
import {app, BrowserWindow, Menu, shell, WebContents} from 'electron'
import * as localShortcut from 'electron-localshortcut'
import DesktopUtils from './DesktopUtils.js'
import u2f from '../misc/u2f-api.js'
Expand All @@ -10,6 +10,8 @@ import url from "url"
import {capitalizeFirstLetter} from "../api/common/utils/StringUtils.js"
import {Keys} from "../api/common/TutanotaConstants"
import type {Shortcut} from "../misc/KeyManager"
import {DesktopConfigHandler} from "./config/DesktopConfigHandler"
import path from "path"

const MINIMUM_WINDOW_SIZE: number = 350

Expand All @@ -22,8 +24,6 @@ export class ApplicationWindow {
_ipc: IPC;
_startFile: string;
_browserWindow: BrowserWindow;
_preloadjs: string;
_desktophtml: string;

_userInfo: ?UserInfo;
_setBoundsTimeout: TimeoutID;
Expand All @@ -33,12 +33,10 @@ export class ApplicationWindow {
_shortcuts: Array<Shortcut>;
id: number;

constructor(wm: WindowManager, preloadjs: string, desktophtml: string, noAutoLogin?: boolean) {
constructor(wm: WindowManager, conf: DesktopConfigHandler, noAutoLogin?: boolean) {
this._userInfo = null
this._ipc = wm.ipc
this._preloadjs = preloadjs
this._desktophtml = desktophtml
this._startFile = DesktopUtils.pathToFileURL(this._desktophtml)
this._startFile = DesktopUtils.pathToFileURL(path.join(app.getAppPath(), conf.get("desktophtml")),)

const isMac = process.platform === 'darwin';
this._shortcuts = [
Expand All @@ -57,7 +55,8 @@ export class ApplicationWindow {
])

console.log("startFile: ", this._startFile)
this._createBrowserWindow(wm)
const preloadPath = path.join(app.getAppPath(), conf.get("preloadjs"))
this._createBrowserWindow(wm, preloadPath)
this._browserWindow.loadURL(
noAutoLogin
? this._startFile + "?noAutoLogin=true"
Expand Down Expand Up @@ -107,7 +106,7 @@ export class ApplicationWindow {
}
}

_createBrowserWindow(wm: WindowManager) {
_createBrowserWindow(wm: WindowManager, preloadPath: string) {
this._browserWindow = new BrowserWindow({
icon: wm.getIcon(),
show: false,
Expand All @@ -123,7 +122,7 @@ export class ApplicationWindow {
// the preload script changes to the web app
contextIsolation: false,
webSecurity: true,
preload: this._preloadjs
preload: preloadPath
}
})
this._browserWindow.setMenuBarVisibility(false)
Expand Down
4 changes: 1 addition & 3 deletions src/desktop/DesktopWindowManager.js
Expand Up @@ -2,7 +2,6 @@

import type {NativeImage, Rectangle} from "electron"
import {app, screen} from "electron"
import path from 'path'
import type {UserInfo} from "./ApplicationWindow"
import {ApplicationWindow} from "./ApplicationWindow"
import type {DesktopConfigHandler} from "./config/DesktopConfigHandler"
Expand Down Expand Up @@ -41,8 +40,7 @@ export class WindowManager {
newWindow(showWhenReady: boolean, noAutoLogin?: boolean): ApplicationWindow {
const w = new ApplicationWindow(
this,
path.join(app.getAppPath(), this._conf.get("preloadjs")),
path.join(app.getAppPath(), this._conf.get("desktophtml")),
this._conf,
noAutoLogin
)
windows.unshift(w)
Expand Down
7 changes: 5 additions & 2 deletions src/desktop/IPC.js
@@ -1,5 +1,6 @@
// @flow
import {app, dialog, ipcMain} from 'electron'
import {lang} from "../misc/LanguageViewModel"
import type {WindowManager} from "./DesktopWindowManager.js"
import {err} from './DesktopErrorHandler.js'
import {defer} from '../api/common/utils/Utils.js'
Expand Down Expand Up @@ -136,8 +137,8 @@ export class IPC {
return this._dl.downloadNative(...args.slice(0, 3))
case 'saveBlob':
// args: [data.name, uint8ArrayToBase64(data.data)]
const filename : string = downcast(args[0])
const data : Uint8Array = base64ToUint8Array(downcast(args[1]))
const filename: string = downcast(args[0])
const data: Uint8Array = base64ToUint8Array(downcast(args[1]))
return this._dl.saveBlob(filename, data, this._wm.get(windowId))
case "aesDecryptFile":
// key, path
Expand Down Expand Up @@ -210,6 +211,8 @@ export class IPC {
this.removeWindow(windowId)
this.addWindow(windowId)
return Promise.resolve()
case 'changeLanguage':
return lang.setLanguage(args[0])
default:
return Promise.reject(new Error(`Invalid Method invocation: ${method}`))
}
Expand Down
10 changes: 9 additions & 1 deletion src/gui/main-styles.js
Expand Up @@ -2,6 +2,7 @@
import {styles} from "./styles"
import {px, size} from "./size"
import {client} from "../misc/ClientDetector"
import {lang} from "../misc/LanguageViewModel"
import {noselect, position_absolute, positionValue} from "./mixins"
import {assertMainOrNodeBoot, isAdminClient, isApp, isDesktop} from "../api/Env"
import {theme} from "./theme.js"
Expand All @@ -14,6 +15,13 @@ export function requiresStatusBarHack() {
return isApp() && client.device === "iPhone" && client.browserVersion < 11
}

function getFonts(): string {
const fonts: Array<string> = ['-apple-system', 'BlinkMacSystemFont', 'Helvetica', 'Arial', 'sans-serif']
if (env.platformId === 'win32' && lang.code === 'ja') fonts.push('SimHei', "黑体")
fonts.push("Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol")
return fonts.join(', ')
}

styles.registerStyle('main', () => {
return {
"#link-tt": isDesktop() ? {
Expand Down Expand Up @@ -108,7 +116,7 @@ styles.registerStyle('main', () => {
'body, button': { // Yes we have to tell buttons separately because browser button styles override general body ones
overflow: 'hidden',
// see: https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/ and github
'font-family': `-apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
'font-family': getFonts(),
'font-size': px(size.font_size_base),
'line-height': size.line_height,
color: theme.content_fg,
Expand Down
9 changes: 9 additions & 0 deletions src/gui/styles.js
Expand Up @@ -49,6 +49,15 @@ class Styles {
}
}

updateStyle(id: string) {
if (!this.initialized || !this.styles.has(id)) {
throw new Error("cannot update nonexistent style " + id)
}
const creator = this.styles.get(id)
log(Cat.css, "update style", id, creator(theme))
this._updateDomStyle(id, creator)
}

_updateDomStyles() {
let time = timer(Cat.css)
Array.from(this.styles.entries()).map((entry) => {
Expand Down
8 changes: 8 additions & 0 deletions src/native/SystemApp.js
Expand Up @@ -2,6 +2,8 @@
import {nativeApp} from "./NativeWrapper"
import {Request} from "../api/common/WorkerProtocol"
import {uriToFileRef} from "./FileApp"
import {isDesktop} from "../api/Env"
import type {Language} from "../misc/LanguageViewModel"

/**
* Open the link
Expand All @@ -21,6 +23,12 @@ export function changeColorTheme(theme: string): Promise<void> {
return nativeApp.invokeNative(new Request('changeTheme', [theme]))
}

export function changeSystemLanguage(language: Language): Promise<void> {
return isDesktop()
? nativeApp.initialized().then(() => nativeApp.invokeNative(new Request('changeLanguage', [language])))
: Promise.resolve()
}

/**
* Get device logs. Returns URI of the file
*/
Expand Down
14 changes: 9 additions & 5 deletions src/settings/AppearanceSettingsViewer.js
@@ -1,6 +1,7 @@
//@flow
import m from "mithril"
import {getLanguage, lang, languageCodeToTag, languages} from "../misc/LanguageViewModel"
import {styles} from "../gui/styles"
import type {DropDownSelectorAttrs} from "../gui/base/DropDownSelectorN"
import {DropDownSelectorN} from "../gui/base/DropDownSelectorN"
import stream from "mithril/stream/stream.js"
Expand All @@ -14,6 +15,7 @@ import {load, update} from "../api/main/Entity"
import {isUpdateForTypeRef} from "../api/main/EventController"
import {UserSettingsGroupRootTypeRef} from "../api/entities/tutanota/UserSettingsGroupRoot"
import {incrementDate} from "../api/common/utils/DateUtils"
import {changeSystemLanguage} from "../native/SystemApp"


export class AppearanceSettingsViewer implements UpdatableSettingsViewer {
Expand All @@ -27,11 +29,13 @@ export class AppearanceSettingsViewer implements UpdatableSettingsViewer {
selectedValue: stream(deviceConfig.getLanguage() || null),
selectionChangedHandler: (value) => {
deviceConfig.setLanguage(value)
if (value) {
lang.setLanguage({code: value, languageTag: languageCodeToTag(value)}).then(m.redraw)
} else {
lang.setLanguage(getLanguage()).then(m.redraw)
}
const newLanguage = value
? {code: value, languageTag: languageCodeToTag(value)}
: getLanguage()
lang.setLanguage(newLanguage)
.then(() => changeSystemLanguage(newLanguage))
.then(() => styles.updateStyle("main"))
.then(m.redraw)
}
}

Expand Down

0 comments on commit 46cc389

Please sign in to comment.