mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Typescript - Add custom title bar
This commit is contained in:
parent
d3b522488a
commit
5db20ef743
@ -1,48 +1,8 @@
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
import { apiCmds, apiName, IApiArgs } from '../common/api-interface';
|
||||
import { logger } from '../common/logger';
|
||||
|
||||
export enum ApiCmds {
|
||||
isOnline,
|
||||
registerLogger,
|
||||
setBadgeCount,
|
||||
badgeDataUrl,
|
||||
activate,
|
||||
registerBoundsChange,
|
||||
registerProtocolHandler,
|
||||
registerActivityDetection,
|
||||
showNotificationSettings,
|
||||
sanitize,
|
||||
bringToFront,
|
||||
openScreenPickerWindow,
|
||||
popupMenu,
|
||||
optimizeMemoryConsumption,
|
||||
optimizeMemoryRegister,
|
||||
setIsInMeeting,
|
||||
setLocale,
|
||||
keyPress,
|
||||
}
|
||||
|
||||
export enum apiName {
|
||||
symphonyApi = 'symphony-api',
|
||||
}
|
||||
|
||||
export interface IApiArgs {
|
||||
cmd: ApiCmds;
|
||||
isOnline: boolean;
|
||||
count: number;
|
||||
dataUrl: string;
|
||||
windowName: string;
|
||||
period: number;
|
||||
reason: string;
|
||||
sources: Electron.DesktopCapturerSource[];
|
||||
id: number;
|
||||
memory: Electron.ProcessMemoryInfo;
|
||||
cpuUsage: Electron.CPUUsage;
|
||||
isInMeeting: boolean;
|
||||
locale: string;
|
||||
keyCode: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure events comes from a window that we have created.
|
||||
* @param {EventEmitter} event node emitter event to be tested
|
||||
@ -108,7 +68,7 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
||||
case ApiCmds.registerBoundsChange:
|
||||
windowMgr.setBoundsChangeWindow(event.sender);
|
||||
break;*/
|
||||
case ApiCmds.registerLogger:
|
||||
case apiCmds.registerLogger:
|
||||
// renderer window that has a registered logger from JS.
|
||||
logger.setLoggerWindow(event.sender);
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BrowserWindow, crashReporter } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
@ -99,7 +100,12 @@ export class WindowHandler {
|
||||
this.loadingWindow.destroy();
|
||||
this.loadingWindow = null;
|
||||
}
|
||||
if (this.mainWindow) this.mainWindow.show();
|
||||
if (this.mainWindow) {
|
||||
this.mainWindow.webContents.insertCSS(
|
||||
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/title-bar.css'), 'utf8').toString(),
|
||||
);
|
||||
this.mainWindow.show();
|
||||
}
|
||||
this.createAboutAppWindow();
|
||||
});
|
||||
return this.mainWindow;
|
||||
|
41
src/common/api-interface.ts
Normal file
41
src/common/api-interface.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export enum apiCmds {
|
||||
isOnline,
|
||||
registerLogger,
|
||||
setBadgeCount,
|
||||
badgeDataUrl,
|
||||
activate,
|
||||
registerBoundsChange,
|
||||
registerProtocolHandler,
|
||||
registerActivityDetection,
|
||||
showNotificationSettings,
|
||||
sanitize,
|
||||
bringToFront,
|
||||
openScreenPickerWindow,
|
||||
popupMenu,
|
||||
optimizeMemoryConsumption,
|
||||
optimizeMemoryRegister,
|
||||
setIsInMeeting,
|
||||
setLocale,
|
||||
keyPress,
|
||||
}
|
||||
|
||||
export enum apiName {
|
||||
symphonyApi = 'symphony-api',
|
||||
}
|
||||
|
||||
export interface IApiArgs {
|
||||
cmd: apiCmds;
|
||||
isOnline: boolean;
|
||||
count: number;
|
||||
dataUrl: string;
|
||||
windowName: string;
|
||||
period: number;
|
||||
reason: string;
|
||||
sources: Electron.DesktopCapturerSource[];
|
||||
id: number;
|
||||
memory: Electron.ProcessMemoryInfo;
|
||||
cpuUsage: Electron.CPUUsage;
|
||||
isInMeeting: boolean;
|
||||
locale: string;
|
||||
keyCode: number;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
export const isDevEnv = process.env.ELECTRON_DEV ?
|
||||
process.env.ELECTRON_DEV.trim().toLowerCase() === 'true' : false;
|
||||
export const isElectronQA = process.env.ELECTRON_QA;
|
||||
export const isElectronQA = !!process.env.ELECTRON_QA;
|
||||
|
||||
export const isMac = (process.platform === 'darwin');
|
||||
export const isWindowsOS = (process.platform === 'win32');
|
||||
|
64
src/common/i18n.ts
Normal file
64
src/common/i18n.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// import { logger } from './logger';
|
||||
import { formatString } from './utils';
|
||||
|
||||
const localeCodeRegex = /^([a-z]{2})-([A-Z]{2})$/;
|
||||
|
||||
export type localeType = 'en-US' | 'ja-JP';
|
||||
|
||||
class Translation {
|
||||
private static translate = (value: string, resource) => resource[value];
|
||||
private locale: localeType = 'en-US';
|
||||
private loadedResource: object = {};
|
||||
|
||||
/**
|
||||
* Apply the locale for translation
|
||||
* @param locale
|
||||
*/
|
||||
public setLocale(locale: localeType): void {
|
||||
const localeMatch: string[] | null = locale.match(localeCodeRegex);
|
||||
if (!locale && (!localeMatch || localeMatch.length < 1)) {
|
||||
// logger.error(`Translation: invalid locale ${locale} found`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.locale = locale;
|
||||
// logger.info(`Translation: locale updated with ${locale}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches and returns the translated value
|
||||
* @param value {string}
|
||||
* @param data {object}
|
||||
*/
|
||||
public t(value: string, data?: object): string {
|
||||
console.log(process.type);
|
||||
if (this.loadedResource && this.loadedResource[this.locale]) {
|
||||
return formatString(Translation.translate(value, this.loadedResource[this.locale]));
|
||||
}
|
||||
const resource = this.loadResource(this.locale);
|
||||
return formatString(resource ? resource[value] : value || value, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the resources dir and returns the data
|
||||
* @param locale
|
||||
*/
|
||||
public loadResource(locale: localeType): object | null {
|
||||
const resourcePath = path.resolve(__dirname, '..', 'locale', `${locale}.json`);
|
||||
|
||||
if (!fs.existsSync(resourcePath)) {
|
||||
// logger.error(`Translation: locale resource path does not exits ${resourcePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.loadedResource[this.locale] = require(resourcePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const i18n = new Translation();
|
||||
|
||||
export { i18n };
|
@ -197,6 +197,4 @@ export class Logger {
|
||||
|
||||
const logger = new Logger();
|
||||
|
||||
export {
|
||||
logger,
|
||||
};
|
||||
export { logger };
|
155
src/locale/en-US.json
Normal file
155
src/locale/en-US.json
Normal file
@ -0,0 +1,155 @@
|
||||
{
|
||||
"About Symphony": "About Symphony",
|
||||
"Actual Size": "Actual Size",
|
||||
"Always on Top": "Always on Top",
|
||||
"Auto Launch On Startup": "Auto Launch On Startup",
|
||||
"BasicAuth": {
|
||||
"Authentication Request": "Authentication Request",
|
||||
"Cancel": "Cancel",
|
||||
"hostname": "hostname",
|
||||
"Invalid user name/password": "Invalid user name/password",
|
||||
"Log In": "Log In",
|
||||
"Password:": "Password:",
|
||||
"Please provide your login credentials for:": "Please provide your login credentials for:",
|
||||
"User name:": "User name:"
|
||||
},
|
||||
"Bring All to Front": "Bring All to Front",
|
||||
"Bring to Front on Notifications": "Bring to Front on Notifications",
|
||||
"Certificate Error": "Certificate Error",
|
||||
"Close": "Close",
|
||||
"Cancel": "Cancel",
|
||||
"ContextMenu": {
|
||||
"Add to Dictionary": "Add to Dictionary",
|
||||
"Copy": "Copy",
|
||||
"Copy Email Address": "Copy Email Address",
|
||||
"Copy Image": "Copy Image",
|
||||
"Copy Image URL": "Copy Image URL",
|
||||
"Copy Link": "Copy Link",
|
||||
"Cut": "Cut",
|
||||
"Inspect Element": "Inspect Element",
|
||||
"Look Up {searchText}": "Look Up \"{searchText}\"",
|
||||
"Open Link": "Open Link",
|
||||
"Paste": "Paste",
|
||||
"Reload": "Reload",
|
||||
"Search with Google": "Search with Google"
|
||||
},
|
||||
"DownloadManager": {
|
||||
"Show in Folder": "Show in Folder",
|
||||
"Reveal in Finder": "Reveal in Finder",
|
||||
"Open": "Open",
|
||||
"Downloaded": "Downloaded",
|
||||
"File not Found": "File not Found",
|
||||
"The file you are trying to open cannot be found in the specified path.": "The file you are trying to open cannot be found in the specified path."
|
||||
},
|
||||
"Copy": "Copy",
|
||||
"Custom": "Custom",
|
||||
"Cut": "Cut",
|
||||
"Delete": "Delete",
|
||||
"Disable Hamburger menu": "Disable Hamburger menu",
|
||||
"Dev Tools disabled": "Dev Tools disabled",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Tools has been disabled. Please contact your system administrator",
|
||||
"Edit": "Edit",
|
||||
"Enable Hamburger menu": "Enable Hamburger menu",
|
||||
"Error loading configuration": "Error loading configuration",
|
||||
"Error loading URL": "Error loading URL",
|
||||
"Error loading window": "Error loading window",
|
||||
"Error setting AutoLaunch configuration": "Error setting AutoLaunch configuration",
|
||||
"Failed!": "Failed!",
|
||||
"Flash Notification in Taskbar": "Flash Notification in Taskbar",
|
||||
"Help": "Help",
|
||||
"Help Url": "https://support.symphony.com",
|
||||
"Symphony Url": "https://symphony.com/en-US",
|
||||
"Hide Others": "Hide Others",
|
||||
"Hide Symphony": "Hide Symphony",
|
||||
"Learn More": "Learn More",
|
||||
"Loading Error": "Loading Error",
|
||||
"Minimize": "Minimize",
|
||||
"Minimize on Close": "Minimize on Close",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "No crashes available to share",
|
||||
"No logs are available to share": "No logs are available to share",
|
||||
"Not Allowed": "Not Allowed",
|
||||
"Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the \"Alt\" key.",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "Bottom Left",
|
||||
"Bottom Right": "Bottom Right",
|
||||
"CANCEL": "CANCEL",
|
||||
"Monitor": "Monitor",
|
||||
"Notification Settings": "Notification Settings",
|
||||
"Notification shown on Monitor: ": "Notification shown on Monitor: ",
|
||||
"OK": "OK",
|
||||
"Position": "Position",
|
||||
"Symphony - Configure Notification Position": "Symphony - Configure Notification Position",
|
||||
"Top Left": "Top Left",
|
||||
"Top Right": "Top Right"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
|
||||
"Paste": "Paste",
|
||||
"Paste and Match Style": "Paste and Match Style",
|
||||
"Permission Denied": "Permission Denied",
|
||||
"Please contact your admin for help": "Please contact your admin for help",
|
||||
"please contact your administrator for more details": "please contact your administrator for more details",
|
||||
"Quit Symphony": "Quit Symphony",
|
||||
"Redo": "Redo",
|
||||
"Refresh app when idle": "Refresh app when idle",
|
||||
"Clear cache and Reload": "Clear cache and Reload",
|
||||
"Relaunch Application": "Relaunch Application",
|
||||
"Relaunch": "Relaunch",
|
||||
"Reload": "Reload",
|
||||
"Renderer Process Crashed": "Renderer Process Crashed",
|
||||
"ScreenPicker": {
|
||||
"Applications": "Applications",
|
||||
"Cancel": "Cancel",
|
||||
"Choose what you'd like to share": "Choose what you'd like to share",
|
||||
"No screens or applications are currently available.": "No screens or applications are currently available.",
|
||||
"Screen Picker": "Screen Picker",
|
||||
"Screens": "Screens",
|
||||
"Select Application": "Select Application",
|
||||
"Select Screen": "Select Screen",
|
||||
"Share": "Share"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Done": "Done",
|
||||
"Erase": "Erase",
|
||||
"Highlight": "Highlight",
|
||||
"Pen": "Pen",
|
||||
"Snipping Tool": "Snipping Tool"
|
||||
},
|
||||
"Select All": "Select All",
|
||||
"Services": "Services",
|
||||
"Show All": "Show All",
|
||||
"Show crash dump in Explorer": "Show crash dump in Explorer",
|
||||
"Show crash dump in Finder": "Show crash dump in Finder",
|
||||
"Show Logs in Explorer": "Show Logs in Explorer",
|
||||
"Show Logs in Finder": "Show Logs in Finder",
|
||||
"SnackBar": {
|
||||
" to exit full screen": " to exit full screen",
|
||||
"esc": "esc",
|
||||
"Press ": "Press "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "Sorry, you are not allowed to access this website",
|
||||
"Speech": "Speech",
|
||||
"Start Speaking": "Start Speaking",
|
||||
"Stop Speaking": "Stop Speaking",
|
||||
"Symphony Help": "Symphony Help",
|
||||
"TitleBar": {
|
||||
"Close": "Close",
|
||||
"Maximize": "Maximize",
|
||||
"Menu": "Menu",
|
||||
"Minimize": "Minimize"
|
||||
},
|
||||
"Title Bar Style": "Title Bar Style",
|
||||
"Toggle Full Screen": "Toggle Full Screen",
|
||||
"Troubleshooting": "Troubleshooting",
|
||||
"Unable to generate crash reports due to ": "Unable to generate crash reports due to ",
|
||||
"Unable to generate logs due to ": "Unable to generate logs due to ",
|
||||
"Undo": "Undo",
|
||||
"Updating Title bar style requires Symphony to relaunch.": "Updating Title bar style requires Symphony to relaunch.",
|
||||
"View": "View",
|
||||
"Window": "Window",
|
||||
"Your administrator has disabled": "Your administrator has disabled",
|
||||
"Zoom": "Zoom",
|
||||
"Zoom In": "Zoom In",
|
||||
"Zoom Out": "Zoom Out"
|
||||
}
|
155
src/locale/ja-JP.json
Normal file
155
src/locale/ja-JP.json
Normal file
@ -0,0 +1,155 @@
|
||||
{
|
||||
"About Symphony": "Symphonyについて",
|
||||
"Actual Size": "実際のサイズ",
|
||||
"Always on Top": "つねに前面に表示",
|
||||
"Auto Launch On Startup": "スタートアップ時に自動起動",
|
||||
"BasicAuth": {
|
||||
"Authentication Request": "認証要求",
|
||||
"Cancel": "キャンセル",
|
||||
"hostname": "ホスト名",
|
||||
"Invalid user name/password": "ユーザー名かパスワード、または両方が無効",
|
||||
"Log In": "ログイン",
|
||||
"Password:": "パスワード:",
|
||||
"Please provide your login credentials for:": "あなたのログイン認証情報を入力してください。",
|
||||
"User name:": "ユーザー名:"
|
||||
},
|
||||
"Bring All to Front": "すべて前面に表示",
|
||||
"Bring to Front on Notifications": "通知時に前面に表示",
|
||||
"Certificate Error": "証明書のエラー",
|
||||
"Close": "閉じる",
|
||||
"Cancel": "キャンセル",
|
||||
"ContextMenu": {
|
||||
"Add to Dictionary": "辞書に追加",
|
||||
"Copy": "コピー",
|
||||
"Copy Email Address": "電子メールアドレスをコピー",
|
||||
"Copy Image": "画像をコピー",
|
||||
"Copy Image URL": "画像のURLをコピー",
|
||||
"Copy Link": "リンクをコピー",
|
||||
"Cut": "切り取り",
|
||||
"Inspect Element": "要素を調査",
|
||||
"Look Up {searchText}": "「{searchText}」を検索",
|
||||
"Open Link": "リンクを開く",
|
||||
"Paste": "貼り付け",
|
||||
"Reload": "再読み込み",
|
||||
"Search with Google": "Googleで検索"
|
||||
},
|
||||
"DownloadManager": {
|
||||
"Show in Folder": "フォルダで見て",
|
||||
"Reveal in Finder": "Finderで明らかにする",
|
||||
"Open": "開いた",
|
||||
"Downloaded": "ダウンロード済み",
|
||||
"File not Found": "ファイルが見つかりません",
|
||||
"The file you are trying to open cannot be found in the specified path.": "開こうとしているファイルが指定されたパスに見つかりません."
|
||||
},
|
||||
"Copy": "コピー",
|
||||
"Custom": "カスタム",
|
||||
"Cut": "切り取り",
|
||||
"Delete": "削除",
|
||||
"Dev Tools disabled": "開発ツールを無効にする",
|
||||
"Dev Tools has been disabled. Please contact your system administrator": "Dev Toolsが無効になっています。システム管理者に連絡してください",
|
||||
"Disable Hamburger menu": "ハンバーガーメニューを無効にする",
|
||||
"Edit": "編集",
|
||||
"Enable Hamburger menu": "ハンバーガーメニューを有効にする",
|
||||
"Error loading configuration": "構成の読み込みエラー",
|
||||
"Error loading URL": "URLの読み込みエラー",
|
||||
"Error loading window": "ウィンドウを読み込みエラー",
|
||||
"Error setting AutoLaunch configuration": "自動起動の構成の設定エラー",
|
||||
"Failed!": "問題が起きました!",
|
||||
"Flash Notification in Taskbar": "タスクバーの通知を点滅",
|
||||
"Help": "ヘルプ",
|
||||
"Help Url": "https://support.symphony.com/hc/ja",
|
||||
"Symphony Url": "https://symphony.com/ja",
|
||||
"Hide Others": "他を隠す",
|
||||
"Hide Symphony": "Symphonyを隠す",
|
||||
"Learn More": "詳細",
|
||||
"Loading Error": "読み込みエラー",
|
||||
"Minimize": "最小化",
|
||||
"Minimize on Close": "閉じるで最小化",
|
||||
"Native": "Native",
|
||||
"No crashes available to share": "共有できるクラッシュはありません",
|
||||
"No logs are available to share": "共有できるログはありません",
|
||||
"Not Allowed": "許可されていませ。",
|
||||
"Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.": "注:ハンバーガーメニューが無効になっている場合、「Alt」キーを押してメインメニューをトリガーすることができます。",
|
||||
"NotificationSettings": {
|
||||
"Bottom Left": "左下",
|
||||
"Bottom Right": "右下",
|
||||
"CANCEL": "キャンセル",
|
||||
"Monitor": "モニター",
|
||||
"Notification Settings": "通知設定",
|
||||
"Notification shown on Monitor: ": "モニターに表示する通知:",
|
||||
"OK": "OK",
|
||||
"Position": "位置",
|
||||
"Symphony - Configure Notification Position": "Symphony - 通知位置の設定",
|
||||
"Top Left": "左上",
|
||||
"Top Right": "右上"
|
||||
},
|
||||
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
|
||||
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
|
||||
"Paste": "貼り付け",
|
||||
"Paste and Match Style": "貼り付けでスタイルを合わせる",
|
||||
"Permission Denied": "アクセス許可が拒否されています",
|
||||
"Please contact your admin for help": "不明な点がある場合は、管理者にお問い合わせください",
|
||||
"please contact your administrator for more details": "詳細は、管理者にお問い合わせください",
|
||||
"Quit Symphony": "Symphonyを終了",
|
||||
"Redo": "やり直し",
|
||||
"Refresh app when idle": "アイドル時にアプリを再表示",
|
||||
"Clear cache and Reload": "キャッシュをクリアしてリロードする",
|
||||
"Relaunch Application": "アプリケーションの再起動",
|
||||
"Relaunch": "「リスタート」",
|
||||
"Reload": "再読み込み",
|
||||
"Renderer Process Crashed": "レンダラープロセスがクラッシュしました",
|
||||
"ScreenPicker": {
|
||||
"Applications": "アプリケーション",
|
||||
"Cancel": "キャンセル",
|
||||
"Choose what you'd like to share": "共有するものを選択する",
|
||||
"No screens or applications are currently available.": "現在利用できる画面およびアプリケーションはありません。",
|
||||
"Screen Picker": "画面の選択",
|
||||
"Screens": "画面",
|
||||
"Select Application": "アプリケーションを選択",
|
||||
"Select Screen": "画面を選択",
|
||||
"Share": "共有"
|
||||
},
|
||||
"ScreenSnippet": {
|
||||
"Done": "完了",
|
||||
"Erase": "消去",
|
||||
"Highlight": "強調",
|
||||
"Pen": "ペン",
|
||||
"Snipping Tool": "切り取りツール"
|
||||
},
|
||||
"Select All": "すべてを選択",
|
||||
"Services": "サービス",
|
||||
"Show All": "すべてを表示",
|
||||
"Show crash dump in Explorer": "Explorerにクラッシュダンプを表示",
|
||||
"Show crash dump in Finder": "ファインダーにクラッシュダンプを表示",
|
||||
"Show Logs in Explorer": "Explorerにログを表示",
|
||||
"Show Logs in Finder": "ファインダーにログを表示",
|
||||
"SnackBar": {
|
||||
" to exit full screen": " を押します",
|
||||
"esc": "esc",
|
||||
"Press ": "全画面表示を終了するには "
|
||||
},
|
||||
"Sorry, you are not allowed to access this website": "申し訳ありませんが、このウェブサイトへのアクセスは許可されていません",
|
||||
"Speech": "スピーチ",
|
||||
"Start Speaking": "スピーチを開始",
|
||||
"Stop Speaking": "スピーチを終了",
|
||||
"Symphony Help": "Symphonyのヘルプ",
|
||||
"TitleBar": {
|
||||
"Close": "閉じる",
|
||||
"Maximize": "最大化する",
|
||||
"Menu": "メニュー",
|
||||
"Minimize": "最小化する"
|
||||
},
|
||||
"Title Bar Style": "タイトルバーのスタイル",
|
||||
"Toggle Full Screen": "画面表示を切り替え",
|
||||
"Troubleshooting": "トラブルシューティング",
|
||||
"Unable to generate crash reports due to ": "クラッシュレポートを生成できません。理由: ",
|
||||
"Unable to generate logs due to ": "ログを生成できません。理由:",
|
||||
"Undo": "元に戻す",
|
||||
"Updating Title bar style requires Symphony to relaunch.": "タイトルバーのスタイルを更新するには、Symphonyが再起動する必要があります。",
|
||||
"View": "ビュー",
|
||||
"Window": "ウインドウ",
|
||||
"Your administrator has disabled": "管理者によて無効にされています",
|
||||
"Zoom": "ズーム",
|
||||
"Zoom In": "ズームイン",
|
||||
"Zoom Out": "ズームアウト"
|
||||
}
|
@ -26,7 +26,7 @@ export default class AboutApp extends React.Component<{}, IState> {
|
||||
/**
|
||||
* main render function
|
||||
*/
|
||||
public render() {
|
||||
public render(): JSX.Element {
|
||||
const { clientVersion, version, buildNumber } = this.state;
|
||||
const appName = remote.app.getName() || 'Symphony';
|
||||
const versionString = `Version ${clientVersion}-${version} (${buildNumber})`;
|
||||
@ -45,7 +45,7 @@ export default class AboutApp extends React.Component<{}, IState> {
|
||||
ipcRenderer.on('about-app-data', this.updateState.bind(this));
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
ipcRenderer.removeListener('open-file-reply', this.updateState);
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ export default class AboutApp extends React.Component<{}, IState> {
|
||||
* @param _event
|
||||
* @param data {Object} { buildNumber, clientVersion, version }
|
||||
*/
|
||||
private updateState(_event, data) {
|
||||
private updateState(_event, data): void {
|
||||
this.setState(data as IState);
|
||||
}
|
||||
}
|
BIN
src/renderer/assets/symphony-title-bar-logo.png
Normal file
BIN
src/renderer/assets/symphony-title-bar-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 872 B |
@ -9,7 +9,7 @@ export default class LoadingScreen extends React.PureComponent {
|
||||
/**
|
||||
* main render function
|
||||
*/
|
||||
public render() {
|
||||
public render(): JSX.Element {
|
||||
const appName = remote.app.getName() || 'Symphony';
|
||||
return (
|
||||
<div className='LoadingScreen'>
|
||||
|
@ -9,7 +9,7 @@ document.addEventListener('DOMContentLoaded', load);
|
||||
/**
|
||||
* Loads the appropriate component
|
||||
*/
|
||||
export function load() {
|
||||
function load() {
|
||||
const query = new URL(window.location.href).searchParams;
|
||||
const componentName = query.get('componentName');
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import WindowsTitleBar from '../renderer/windows-title-bar';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', load);
|
||||
|
||||
/**
|
||||
* Injects custom title bar to the document body
|
||||
*/
|
||||
function load() {
|
||||
const element = React.createElement(WindowsTitleBar);
|
||||
ReactDOM.render(element, document.body);
|
||||
}
|
107
src/renderer/styles/title-bar.css
Normal file
107
src/renderer/styles/title-bar.css
Normal file
@ -0,0 +1,107 @@
|
||||
.title-bar {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
background: rgba(74,74,74,1);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding-left: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
-webkit-app-region: drag;
|
||||
-webkit-user-select: none;
|
||||
box-sizing: content-box;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.hamburger-menu-button {
|
||||
color: rgba(255,255,255,1);
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
height: 32px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-image: initial;
|
||||
display: inline-grid;
|
||||
border-radius: 0;
|
||||
padding: 11px;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.hamburger-menu-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
height: 32px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.title-bar-title {
|
||||
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
||||
color: white;
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.title-bar-button-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
right: 0;
|
||||
color: rgba(255,255,255,1);
|
||||
-webkit-app-region: no-drag;
|
||||
text-align: center;
|
||||
width: 45px;
|
||||
height: 32px;
|
||||
background: rgba(74,74,74,1);
|
||||
margin: 0;
|
||||
box-sizing: border-box !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.title-bar-button {
|
||||
color: rgba(255,255,255,1);
|
||||
text-align: center;
|
||||
width: 45px;
|
||||
height: 32px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-image: initial;
|
||||
display: inline-grid;
|
||||
border-radius: 0;
|
||||
padding: 10px 15px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.title-bar-button:hover {
|
||||
background-color: rgba(51,51,51,1);
|
||||
}
|
||||
|
||||
.title-bar-button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.title-bar-button-container:hover {
|
||||
background-color: rgba(51,51,51,1);
|
||||
}
|
||||
|
||||
.window-border {
|
||||
border-left: 1px solid rgba(74,74,74,1);
|
||||
border-right: 1px solid rgba(74,74,74,1);
|
||||
}
|
||||
|
||||
.bottom-window-border {
|
||||
position: fixed;
|
||||
border-bottom: 1px solid rgba(74,74,74,1);
|
||||
width: 100%;
|
||||
z-index: 3000;
|
||||
bottom: 0;
|
||||
}
|
249
src/renderer/windows-title-bar.tsx
Normal file
249
src/renderer/windows-title-bar.tsx
Normal file
@ -0,0 +1,249 @@
|
||||
import { ipcRenderer, remote } from 'electron';
|
||||
import * as React from 'react';
|
||||
|
||||
import { apiCmds, apiName } from '../common/api-interface';
|
||||
import { i18n } from '../common/i18n';
|
||||
|
||||
interface IState {
|
||||
isMaximized: boolean;
|
||||
isFullScreen: boolean;
|
||||
titleBarHeight: string;
|
||||
}
|
||||
|
||||
export default class WindowsTitleBar extends React.Component<{}, IState> {
|
||||
private window: Electron.BrowserWindow;
|
||||
private readonly eventHandlers = {
|
||||
onClose: () => this.close(),
|
||||
onMaximize: () => this.maximize(),
|
||||
onMinimize: () => this.minimize(),
|
||||
onShowMenu: () => this.showMenu(),
|
||||
onUnmaximize: () => this.unmaximize(),
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.window = remote.getCurrentWindow();
|
||||
this.state = {
|
||||
isFullScreen: this.window.isFullScreen(),
|
||||
isMaximized: this.window.isMaximized(),
|
||||
titleBarHeight: '32px',
|
||||
};
|
||||
// Adds borders to the window
|
||||
this.addWindowBorders();
|
||||
|
||||
this.renderMaximizeButtons = this.renderMaximizeButtons.bind(this);
|
||||
// Event to capture and update icons
|
||||
this.window.on('maximize', () => this.updateState({ isMaximized: true }));
|
||||
this.window.on('unmaximize', () => this.updateState({ isMaximized: false }));
|
||||
this.window.on('enter-full-screen', () => this.updateState({ isFullScreen: true }));
|
||||
this.window.on('leave-full-screen', () => this.updateState({ isFullScreen: false }));
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const contentWrapper = document.getElementById('content-wrapper');
|
||||
if (contentWrapper) {
|
||||
if (this.state.isFullScreen) {
|
||||
contentWrapper.style.marginTop = '0px';
|
||||
document.body.style.removeProperty('margin-top');
|
||||
} else {
|
||||
contentWrapper.style.marginTop = this.state.titleBarHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillMount() {
|
||||
this.window.removeListener('maximize', this.updateState);
|
||||
this.window.removeListener('unmaximize', this.updateState);
|
||||
this.window.removeListener('enter-full-screen', this.updateState);
|
||||
this.window.removeListener('leave-full-screen', this.updateState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the custom title bar
|
||||
*/
|
||||
public render(): JSX.Element | null {
|
||||
|
||||
const { isFullScreen } = this.state;
|
||||
const style = { display: isFullScreen ? 'none' : 'flex' };
|
||||
|
||||
return (
|
||||
<div className='title-bar'
|
||||
onDoubleClick={this.state.isMaximized ? this.eventHandlers.onUnmaximize : this.eventHandlers.onMaximize}
|
||||
style={style}
|
||||
>
|
||||
<div className='title-bar-button-container'>
|
||||
<button
|
||||
title={i18n.t('Menu')}
|
||||
className='hamburger-menu-button'
|
||||
onClick={this.eventHandlers.onShowMenu}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
>
|
||||
<svg x='0px' y='0px' viewBox='0 0 15 10'>
|
||||
<rect fill='rgba(255, 255, 255, 0.9)' width='15' height='1'/>
|
||||
<rect fill='rgba(255, 255, 255, 0.9)' y='4' width='15' height='1'/>
|
||||
<rect fill='rgba(255, 255, 255, 0.9)' y='8' width='152' height='1'/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className='title-container'>
|
||||
<img src='./assets/symphony-title-bar-logo.png'/>
|
||||
<p className='title-bar-title'>{document.title || 'Symphony'}</p>
|
||||
</div>
|
||||
<div className='title-bar-button-container'>
|
||||
<button
|
||||
className='title-bar-button'
|
||||
title={i18n.t('Minimize')}
|
||||
onClick={this.eventHandlers.onMinimize}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
>
|
||||
<svg x='0px' y='0px' viewBox='0 0 14 1'>
|
||||
<rect fill='rgba(255, 255, 255, 0.9)' width='14' height='0.6'/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className='title-bar-button-container'>
|
||||
{this.renderMaximizeButtons()}
|
||||
</div>
|
||||
<div className='title-bar-button-container'>
|
||||
<button
|
||||
className='title-bar-button'
|
||||
title={i18n.t('Close')}
|
||||
onClick={this.eventHandlers.onClose}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
>
|
||||
<svg x='0px' y='0px' viewBox='0 0 14 10.2'>
|
||||
<polygon
|
||||
fill='rgba(255, 255, 255, 0.9)'
|
||||
points='10.2,0.7 9.5,0 5.1,4.4 0.7,0 0,0.7 4.4,5.1 0,9.5 0.7,10.2 5.1,5.8 9.5,10.2 10.2,9.5 5.8,5.1 '
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders maximize or minimize buttons based on fullscreen state
|
||||
*/
|
||||
public renderMaximizeButtons(): JSX.Element {
|
||||
const { isMaximized } = this.state;
|
||||
|
||||
if (isMaximized) {
|
||||
return (
|
||||
<button
|
||||
className='title-bar-button'
|
||||
title={i18n.t('Maximize')}
|
||||
onClick={this.eventHandlers.onUnmaximize}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
>
|
||||
<svg x='0px' y='0px' viewBox='0 0 14 10.2'>
|
||||
<path
|
||||
fill='rgba(255, 255, 255, 0.9)'
|
||||
d='M2.1,0v2H0v8.1h8.2v-2h2V0H2.1z M7.2,9.2H1.1V3h6.1V9.2z M9.2,7.1h-1V2H3.1V1h6.1V7.1z'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<button
|
||||
className='title-bar-button'
|
||||
title={i18n.t('unMaximize')}
|
||||
onClick={this.eventHandlers.onMaximize }
|
||||
onMouseDown={this.handleMouseDown}
|
||||
>
|
||||
<svg x='0px' y='0px' viewBox='0 0 14 10.2'>
|
||||
<path
|
||||
fill='rgba(255, 255, 255, 0.9)'
|
||||
d='M0,0v10.1h10.2V0H0z M9.2,9.2H1.1V1h8.1V9.2z'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that closes the browser window
|
||||
*/
|
||||
public close(): void {
|
||||
if (this.isValidWindow()) {
|
||||
this.window.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that minimizes the browser window
|
||||
*/
|
||||
public minimize(): void {
|
||||
if (this.isValidWindow()) {
|
||||
this.window.minimize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that maximize the browser window
|
||||
*/
|
||||
public maximize(): void {
|
||||
if (this.isValidWindow()) {
|
||||
console.log(this.window.maximize());
|
||||
this.window.maximize();
|
||||
this.setState({ isMaximized: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that unmaximize the browser window
|
||||
*/
|
||||
public unmaximize(): void {
|
||||
if (this.isValidWindow()) {
|
||||
this.window.isFullScreen() ? this.window.setFullScreen(false) : this.window.unmaximize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that popup the application menu
|
||||
*/
|
||||
public showMenu(): void {
|
||||
if (this.isValidWindow()) {
|
||||
ipcRenderer.send(apiName.symphonyApi, {
|
||||
cmd: apiCmds.popupMenu,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* verifies if the this.window is valid and is not destroyed
|
||||
*/
|
||||
public isValidWindow(): boolean {
|
||||
return (this.window && !this.window.isDestroyed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent default to make sure buttons don't take focus
|
||||
* @param e
|
||||
*/
|
||||
private handleMouseDown(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds borders to the edges of the window chrome
|
||||
*/
|
||||
private addWindowBorders() {
|
||||
const borderBottom = document.createElement('div');
|
||||
borderBottom.className = 'bottom-window-border';
|
||||
|
||||
document.body.appendChild(borderBottom);
|
||||
document.body.classList.add('window-border');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state with the give value
|
||||
* @param state
|
||||
*/
|
||||
private updateState(state: Partial<IState>) {
|
||||
this.setState((s) => Object.assign(s, state));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user