Typescript - Add custom title bar

This commit is contained in:
Kiran Niranjan 2018-11-20 21:52:35 +05:30
parent d3b522488a
commit 5db20ef743
15 changed files with 802 additions and 53 deletions

View File

@ -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;

View File

@ -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;

View 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;
}

View File

@ -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
View 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 };

View File

@ -197,6 +197,4 @@ export class Logger {
const logger = new Logger();
export {
logger,
};
export { logger };

155
src/locale/en-US.json Normal file
View 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
View 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": "ズームアウト"
}

View File

@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

View File

@ -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'>

View File

@ -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');

View File

@ -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);
}

View 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;
}

View 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));
}
}