一名UI設計師的Electron學習之路(三)——記事本UI優化版

前言

一個小小的記事本,除了基本的功能外,若是須要在用戶體驗方面作的更好,不少小細節須要進行考慮,如關閉前自動保存窗體信息,保存皮膚設置,以及快捷鍵功能等等。css

效果圖

效果圖.gif

主要優化方向

  1. 模擬主菜單(帶快捷鍵)
  2. 換膚
  3. 窗體信息保存

開發思路

默認的主菜單在electron隱藏邊框後,依然能夠使用快捷鍵進行操做,在此基礎上直接寫一個具備點擊下拉效果的菜單便可。窗體的長寬、位置、最大化這幾個信息獲取後利用nodejs的fs模塊進行保存,保存爲json格式便於讀取和調用。換膚功能則採用替換css樣式文件。這就是三個主要功能的開發思路,其餘小細節在開發中逐步優化。html

代碼

主進程代碼node

// main.js
const {app, BrowserWindow, ipcMain, Menu} = require('electron');
const path = require('path');
const fs = require('fs'); // 引入 NodeJS 的 fs 模塊


// 主菜單模板
const menuTemplate = [
  {
    label: ' 文件 ',
    submenu: [
      { 
        label: '新建', 
        accelerator: 'CmdOrCtrl+N', 
        click: function() {
          mainWindow.webContents.send('action', 'new') 
        } 
      },
      { 
        label: '打開', 
        accelerator: 'CmdOrCtrl+O', 
        click: function() {
          mainWindow.webContents.send('action', 'open') 
        } 
      },
      { 
        label: '保存', 
        accelerator: 'CmdOrCtrl+S', 
        click: function() {
          mainWindow.webContents.send('action', 'save') 
        } 
      },
      { 
        label: '另存爲...  ', 
        accelerator: 'CmdOrCtrl+Shift+S', 
        click: function() {
          mainWindow.webContents.send('action', 'save-as') 
        } 
      },
      { 
        type: 'separator' 
      },
      {
        label: '退出',
        click: function() {
          mainWindow.webContents.send('action', 'exit') 
        }
      }
    ]
  },
  {
    label: ' 編輯 ',
    submenu: [
      { label: '返回', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
      { label: '重作', accelerator: 'CmdOrCtrl+Y', role: 'redo' },
      { type: 'separator' },  //分隔線
      { label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' },
      { label: '複製', accelerator: 'CmdOrCtrl+C', role: 'copy' },
      { label: '粘貼', accelerator: 'CmdOrCtrl+V', role: 'paste' },
      { label: '刪除', accelerator: 'CmdOrCtrl+D', role: 'delete' },
      { type: 'separator' },  //分隔線
      { label: '全選', accelerator: 'CmdOrCtrl+A', role: 'selectall' },
      { label: 'DevTools', accelerator: 'CmdOrCtrl+I', 
          click: function() {
            mainWindow.webContents.openDevTools();
        }
      },
      { accelerator: 'CmdOrCtrl+R', role: 'reload' }
    ]
  }
];

// 主窗體
let mainWindow;
// 安全退出初始化
let safeExit = false;

// 構建主菜單
let menu = Menu.buildFromTemplate (menuTemplate);
Menu.setApplicationMenu (menu);

// 讀取窗體保存數據
var data = fs.readFileSync('./data.json');
var myData = JSON.parse(data);

// 主窗體初始化
function createWindow() {
  mainWindow = new BrowserWindow({
    x: myData.positionX,
    y: myData.positionY,
    width: myData.width,
    height: myData.height,
    minWidth: 400,
    minHeight: 300,
    frame: false,
    backgroundColor: '#000000',
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true
    }
  });

  mainWindow.once('ready-to-show', () => {
    mainWindow.show();
  });

  // 加載頁面內容
  mainWindow.loadFile('index.html');

  // 開發者工具
  //mainWindow.webContents.openDevTools();

  // 窗體生命週期 close 操做
  mainWindow.on('close', (e) => {
    if(!safeExit) {
      e.preventDefault();
    }
    mainWindow.webContents.send('action', 'exit');
  });
  // 窗體生命週期 closed 操做
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
}

// 程序生命週期 ready
app.on('ready', createWindow);
// 程序生命週期 window-all-closed
app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') app.quit();
});
// 程序生命週期 activate
app.on('activate', function() {
  if (mainWindow === null) createWindow();
});



// 窗體操做
ipcMain.on('reqaction', (event, arg) => {
  switch(arg) {
    case 'exit': // 接收退出命令
      safeExit = true;
      app.quit();
      break;
    case 'win-min': // 接收最小化命令
      mainWindow.minimize();
      break;
    case 'win-max': // 接收最大化命令
      if(mainWindow.isMaximized()) {
        mainWindow.restore();  
      } else {
        mainWindow.maximize(); 
      }
      break;
  }
});

渲染進程代碼git

// renderer.js
const ipcRenderer = require('electron').ipcRenderer; // electron 通訊模塊
const remote = require('electron').remote; // electron 主進程與渲染進程通訊模塊
const Menu = remote.Menu; // electron renderer進程的菜單模塊
const dialog = remote.dialog; // electron 對話框模塊
const fs = require('fs'); // 引入 NodeJS 的 fs 模塊
const shell = require('electron').shell;


// 讀取保存數據
var data = fs.readFileSync('./data.json');
var myData = JSON.parse(data);
var themes = myData.theme;
if(themes == 'dark') {
    document.getElementById('theme_css').href = './styleDark.css';
} else {
    document.getElementById('theme_css').href = './style.css';
}
if(myData.isFull) {
    ipcRenderer.send('reqaction', 'win-max');
}


// 初始化基本參數
let isSave = true; // 初始狀態無需保存
let txtEditor = document.getElementById('txtEditor'); // 獲取文本框對象
let currentFile = null; // 初始狀態無文件路徑
let isQuit = true; // 初始狀態可正常退出


// 右鍵菜單模板
const contextMenuTemplate = [
    { label: '返回', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
    { label: '重作', accelerator: 'CmdOrCtrl+Y', role: 'redo' },
    { type: 'separator' },  //分隔線
    { label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' },
    { label: '複製', accelerator: 'CmdOrCtrl+C', role: 'copy' },
    { label: '粘貼', accelerator: 'CmdOrCtrl+V', role: 'paste' },
    { label: '刪除', accelerator: 'CmdOrCtrl+D', role: 'delete' },
    { type: 'separator' },  //分隔線
    { label: '全選', accelerator: 'CmdOrCtrl+A', role: 'selectall' },
    { type: 'separator' },  //分隔線
    { label: 'DevTools', accelerator: 'CmdOrCtrl+I', 
        click: function() {
            remote.getCurrentWindow().openDevTools();
      }
    },
    { accelerator: 'CmdOrCtrl+R', role: 'reload' }
];
// 構建右鍵菜單
const contextMenu = Menu.buildFromTemplate(contextMenuTemplate);
txtEditor.addEventListener('contextmenu', (e) => {
    e.preventDefault();
    contextMenu.popup(remote.getCurrentWindow());
});


// 右上角窗體操做按鈕
function winCtrlBtn(id) {
    switch(id) {
        case 'win_min': // 最小化
            ipcRenderer.send('reqaction', 'win-min');
            break;
        case 'win_max': // 最大化
            ipcRenderer.send('reqaction', 'win-max');
            break;
        case 'win_close': // 退出
            askSaveNeed(); // 保證安全退出
            saveWinData(); // 保存窗體數據
            if(isQuit) { // 正常退出
                ipcRenderer.sendSync('reqaction', 'exit');
            }
            isQuit = true; // 復位正常退出
            break;
    }
}
// 監聽窗口變化改變放大縮小按鈕的圖標
window.onresize = function () {
    if(remote.getCurrentWindow().isMaximized()) {
        document.getElementById('win_max').style.background = "url(images/ctrl-btn.png) no-repeat 0 -60px";
    }else {
        document.getElementById('win_max').style.background = "url(images/ctrl-btn.png) no-repeat 0 -30px";
    }
}

// 檢測編輯器是否有內容更新,統計字數
txtEditor.oninput = (e) => {
    if (isSave) {
        document.title += ' *';
        document.getElementById("mainTitle").innerHTML = document.title;
    }
    isSave = false;
    // 字數統計
    wordsCount();
}


// 菜單操做
ipcRenderer.on('action', (event, arg) => {
    switch(arg) {
        case 'new': // 新建文檔
            askSaveNeed();
            initDoc();
            break;
        case 'open': // 打開文檔
            askSaveNeed();
            openFile();
            wordsCount();
            break;
        case 'save': // 保存當前文檔
            saveCurrentDoc();
            break;
        case 'save-as': // 另存爲當前文檔
            currentFile = null;
            saveCurrentDoc();
            break;
        case 'exit': // 退出
            askSaveNeed(); // 安全退出
            saveWinData(); // 保存窗體數據
            if(isQuit) { // 正常退出
                ipcRenderer.sendSync('reqaction', 'exit');
            }
            isQuit = true; // 復位正常退出
            break;
    }
});


// 初始化文檔
function initDoc() {
    currentFile = null;
    txtEditor.value = '';
    document.title = 'Notepad - Untitled';
    document.getElementById("mainTitle").innerHTML = document.title;
    isSave = true;
    document.getElementById("txtNum").innerHTML = 0;
}


// 詢問是否保存命令
function askSaveNeed() {
    // 檢測是否須要執行保存命令
    if (isSave) {
        return;
    }
    // 彈窗類型爲 message
    const options = {
        type: 'question',
        message: '請問是否保存當前文檔?',
        buttons: [ 'Yes', 'No', 'Cancel']
    }
    // 處理彈窗操做結果
    const selection = dialog.showMessageBoxSync(remote.getCurrentWindow(), options);
    // 按鈕 yes no cansel 分別爲 [0, 1, 2]
    if (selection == 0) {
        saveCurrentDoc();
    } else if(selection == 1) {
        console.log('Cancel and Quit!');
    } else { // 點擊 cancel 或者關閉彈窗則禁止退出操做
        console.log('Cancel and Hold On!');
        isQuit = false; // 阻止執行退出
    }
}


// 保存文檔,判斷新文檔or舊文檔
function saveCurrentDoc() {
    // 新文檔則執行彈窗保存操做
    if(!currentFile) {
        const options = {
            title: 'Save',
            filters: [
                { name: 'Text Files', extensions: ['txt', 'js', 'html', 'md'] },
                { name: 'All Files', extensions: ['*'] }
            ]
        }
        const paths = dialog.showSaveDialogSync(remote.getCurrentWindow(), options);
        if(paths) {
            currentFile = paths;
        }
    }
    // 舊文檔直接執行保存操做
    if(currentFile) {
        const txtSave = txtEditor.value;
        saveText(currentFile, txtSave);
        isSave = true;
        document.title = "Notepad - " + currentFile;
        document.getElementById("mainTitle").innerHTML = document.title;
    }

}


// 選擇文檔路徑
function openFile() {
    // 彈窗類型爲openFile
    const options = {
        filters: [
            { name: 'Text Files', extensions: ['txt', 'js', 'html', 'md'] },
            { name: 'All Files', extensions: ['*'] }
        ],
        properties: ['openFile']
    }
    // 處理彈窗結果
    const file = dialog.showOpenDialogSync(remote.getCurrentWindow(), options);
    if(file) {
        currentFile = file[0];
        const txtRead = readText(currentFile);
        txtEditor.value = txtRead;
        document.title = 'Notepad - ' + currentFile;
        document.getElementById("mainTitle").innerHTML = document.title;
        isSave = true;
    }

}


// 執行保存的方法
function saveText( file, text ) {
    fs.writeFileSync( file, text );
}


// 讀取文檔方法
function readText(file) {
    return fs.readFileSync(file, 'utf8');
}


// 字數統計
function wordsCount() {
    var str = txtEditor.value;
    sLen = 0;
    try{
        //先將回車換行符作特殊處理
           str = str.replace(/(\r\n+|\s+| +)/g,"龘");
        //處理英文字符數字,連續字母、數字、英文符號視爲一個單詞
        str = str.replace(/[\x00-\xff]/g,"m");    
        //合併字符m,連續字母、數字、英文符號視爲一個單詞
        str = str.replace(/m+/g,"*");
           //去掉回車換行符
        str = str.replace(/龘+/g,"");
        //返回字數
        sLen = str.length;
    }catch(e){
        console.log(e);
    }
    // 刷新當前字數統計值到頁面中
    document.getElementById("txtNum").innerHTML = sLen;
}


// 拖拽讀取文檔
const dragContent = document.querySelector('#txtEditor');
// 阻止 electron 默認事件
dragContent.ondragenter = dragContent.ondragover = dragContent.ondragleave = function() {
    return false;
}
// 拖拽事件執行
dragContent.ondrop = function(e) {
    e.preventDefault(); // 阻止默認事件
    askSaveNeed();
    currentFile = e.dataTransfer.files[0].path; // 獲取文檔路徑
    const txtRead = readText(currentFile);
    txtEditor.value = txtRead;
    document.title = 'Notepad - ' + currentFile;
    document.getElementById("mainTitle").innerHTML = document.title;
    isSave = true;
    wordsCount();
}



// 主菜單事件
function showList(o) {
    hideList("dropdown-content" + o.id);
    document.getElementById("dropdown-" + o.id).classList.toggle("show");
    document.getElementById("a").setAttribute("onmousemove","showList(this)");
    document.getElementById("b").setAttribute("onmousemove","showList(this)");
    document.getElementById("c").setAttribute("onmousemove","showList(this)");
    // 判斷點擊背景採用的皮膚顏色
    var clickColor;
    if(themes == 'dark') {
        clickColor = '#505050';
    } else {
        clickColor = '#d5e9ff';
    }
    // 點擊狀態下背景色固定
    if(o.id == 'a') {
        document.getElementById('a').style.background = clickColor;
        document.getElementById('b').style.background = "";
        document.getElementById('c').style.background = "";
    }
    if(o.id == 'b') {
        document.getElementById('a').style.background = "";
        document.getElementById('b').style.background = clickColor;
        document.getElementById('c').style.background = "";
    }
    if(o.id == 'c') {
        document.getElementById('a').style.background = "";
        document.getElementById('b').style.background = "";
        document.getElementById('c').style.background = clickColor;
    }
}
 
// 主菜單隱藏操做
function hideList(option) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    for (var i = 0; i < dropdowns.length; i++) {
        var openDropdown = dropdowns[i];
        if (openDropdown.id != option) {
            if (openDropdown.classList.contains('show')) {
                openDropdown.classList.remove('show');
            }
        }
    }
}

// 主菜單點擊復位操做
window.onclick = function(e) {
    if (!e.target.matches('.dropbtn')) {
        hideList("");
        document.getElementById("a").setAttribute("onmousemove","");
        document.getElementById("b").setAttribute("onmousemove","");
        document.getElementById("c").setAttribute("onmousemove","");
        document.getElementById("a").style.background = "";
        document.getElementById("b").style.background = "";
        document.getElementById("c").style.background = "";
    }
}

// 主菜單快捷鍵操做
function hotkey() {
    var key = window.event.keyCode;
    var keyCtrl;
    if((key == 70)&&(event.altKey)) {
        keyCtrl = document.getElementById("a");
        showList(keyCtrl);
    }
    if((key == 69)&&(event.altKey)) {
        keyCtrl = document.getElementById("b");
        showList(keyCtrl);
    }
    if((key == 72)&&(event.altKey)) {
        keyCtrl = document.getElementById("c");
        showList(keyCtrl);
    }
}
document.onkeydown = hotkey;


// 主菜單文件操做
function menuClick(arg) {
    switch(arg) {
        case 'new': // 新建文檔
            askSaveNeed();
            initDoc();
            break;
        case 'open': // 打開文檔
            askSaveNeed();
            openFile();
            wordsCount();
            break;
        case 'save': // 保存當前文檔
            saveCurrentDoc();
            break;
        case 'save-as': // 另存爲當前文檔
            currentFile = null;
            saveCurrentDoc();
            break;
    }
}

// 主菜單編輯操做
function docCommand(arg) {
    switch(arg) {
        case 'undo': // 返回
            document.execCommand('Undo');
            break;
        case 'redo': // 重作
            document.execCommand('Redo');
            break;
        case 'cut': // 剪切
            document.execCommand('Cut', false, null);
            break;
        case 'copy': // 複製
            document.execCommand('Copy', false, null);
            break;
        case 'paste': // 粘貼
            document.execCommand('Paste', false, null);
            break;
        case 'delete': // 刪除
            document.execCommand('Delete', false, null);
            break;
        case 'seletAll': // 全選
            document.execCommand('selectAll');
            break;
    }
}

// 主菜單中關於跳轉
function aboutMe() {
    shell.openExternal('https://segmentfault.com/u/shaomeng');
}

//換膚
function theme() {
    if(themes == 'normal') {
        document.getElementById('theme_css').href = './styleDark.css';
        themes = 'dark';
    } else {
        document.getElementById('theme_css').href = './style.css';
        themes = 'normal';
    }
}

// 保存窗體相關數據
function saveWinData() {
    // 獲取窗體相關數據
    var dF = remote.getCurrentWindow().isMaximized();
    var dX = dF == true ? myData.positionX : remote.getCurrentWindow().getPosition()[0];
    var dY = dF == true ? myData.positionY : remote.getCurrentWindow().getPosition()[1];
    var dWidth = dF == true ? myData.width : remote.getCurrentWindow().getSize()[0];
    var dHeight = dF == true ? myData.height : remote.getCurrentWindow().getSize()[1];
    // 數據合集
    var obj = {
        "isFull": dF,
        "positionX": dX,
        "positionY": dY,
        "width": dWidth,
        "height": dHeight,
        "theme": themes
    }
    // 格式化 json 數據
    var d = JSON.stringify(obj, null, '\t');
    // 寫入文本
    fs.writeFileSync('./data.json', d);
}

頁面代碼github

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link id="theme_css" rel="stylesheet" href="./style.css" type="text/css" media="screen"/>
    <title>Notepad</title>
  </head>
  <body>
    <div class="header">
      <div class="onTop"></div>
      <div class="menuList">
          <ul>
              <li class="dropdown">
                  <p id="a" class="dropbtn" onclick="showList(this)">文件(F)</p>
                  <div class="dropdown-content" id="dropdown-a">
                      <p onclick="menuClick('new')">新建<span class="keyQ">Ctrl+N</span></p>
                      <p onclick="menuClick('open')">打開<span class="keyQ">Ctrl+O</span></p>
                      <p onclick="menuClick('save')">保存<span class="keyQ">Ctrl+S</span></p>
                      <p onclick="menuClick('save-as')" class="menuS">另存爲... <span class="keyQ">Ctrl+Shift+S</span></p>
                      <p onclick="winCtrlBtn('win_close')">退出<span class="keyQ">Ctrl+S</span></p>
                  </div>
              </li>
              <li class="dropdown">
                  <p id="b" class="dropbtn" onclick="showList(this)">編輯(E)</p>
                  <div class="dropdown-content" id="dropdown-b">
                      <p onclick="docCommand('undo')">返回<span class="keyQ">Ctrl+Z</span></p>
                      <p onclick="docCommand('redo')" class="menuS">重作<span class="keyQ">Ctrl+Y</span></p>
                      <p onclick="docCommand('cut')">剪切<span class="keyQ">Ctrl+X</span></p>
                      <p onclick="docCommand('copy')">複製<span class="keyQ">Ctrl+C</span></p>
                      <p onclick="docCommand('paste')">粘貼<span class="keyQ">Ctrl+V</span></p>
                      <p onclick="docCommand('delete')" class="menuS">刪除<span class="keyQ">Ctrl+D</span></p>
                      <p onclick="docCommand('seletAll')">全選<span class="keyQ">Ctrl+A</span></p>
                  </div>
              </li>
              <li clcass="dropdown">
                <p id="c" class="dropbtn" onclick="showList(this)">幫助(H)</p>
                  <div class="dropdown-content" id="dropdown-c">
                    <p onclick="theme()">換膚</p>
                    <p onclick="aboutMe()">關於...</p>
                  </div>
              </li>
          </ul>
      </div>
      <div id="mainTitle" class="mainTitle">Notepad</div>
      <div class="ctrlBtn">
        <p id="win_min" class="win_min" onclick="winCtrlBtn('win_min')"></p>
        <p id="win_max" class="win_max" onclick="winCtrlBtn('win_max')"></p>
        <p id="win_close" class="win_close" onclick="winCtrlBtn('win_close')"></p>
      </div>
    </div>
    <div class="txtBox"><textarea class="txtEditor" id="txtEditor"></textarea></div>
    <div class="bottom">字數:<span class="txtNum" id="txtNum">0</span></div>
    <script src="./renderer.js"></script>
  </body>
</html>

CSS樣式(白色)web

/*style.css*/
body, html {
    margin:0;
    padding:0;
    height: 100%; 
    overflow: hidden;
}

.txtBox {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 30px;
    padding-bottom: 50px;
    box-sizing: border-box;
}

.txtEditor{
    width: 100%;
    height: 100%;
    font-size: 16px;
    resize:none;
    outline:none;
    border:0px;
    box-sizing: border-box;
    cursor:auto;
    overflow-y:scroll;
}

.txtEditor:focus{
    border:0px;
    outline:none;
}

.bottom {
    height: 19px;
    width: 100%;
    font-size: 12px;
    color: #666666;
    text-align: right;
    position: absolute;
    bottom: 0;
    border-top: 1px solid #cccccc;
    background-color: #f2f2f2;
}

.txtNum {
    padding-right: 20px;
}


.header {
    -webkit-user-select: none;
    -webkit-app-region: drag;
    height: 29px;
    width: 100%;
    background: #ffffff url(images/logo-24.svg) no-repeat 2px 2px;
    border-bottom: 1px solid #cccccc;
    position: absolute;
    top: 0;
    z-index:1;
}

/*Menu************************************************/
.menuList {
    width: 210px;
    margin: 0;
    padding: 0;
    float: left;
    display: block;
    position: absolute;
    left: 0;
    top: 0;
}
ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    line-height: 28px;
    font-size: 14px;
    margin-left: 34px;
}
li {
    float: left;
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
}
li p, .dropbtn {
    display: inline-block;
    color: #000000;
    text-align: center;
    padding: 1px 6px;
    text-decoration: none;
    margin: 0;
}
li p:hover, .dropdown:hover .dropbtn {
    background-color: #d5e9ff;
}
li.dropdown {
    display: inline-block;
}
.dropdown-content {
    display: none;
    position: absolute;
    background-color: #fafafa;
    min-width: 120px;
    box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.3);
}
.dropdown-content p {
    color: #000000;
    padding: 1px 16px;
    margin: 0;
    text-decoration: none;
    display: block;
    text-align: left;
}
.dropdown-content p:hover {
    background-color: #d5e9ff;
}
.show {
    display: block;
}

.keyQ{
    float: right;
    padding-left: 10px;
    font-size: 13px;
    color: #707070;
}
.menuS{
    border-bottom: 1px solid #dbdbdb;
}

.ctrlBtn {
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
    height: 29px;
    width: 120px;
    display: block;
    position: absolute;
    right: 0;
    top: 0;
}
.ctrlBtn p {
    width: 40px;
    height: 29px;
    float: left;
    margin: 0;
    padding: 0;
    line-height: 29px;
    display: block;
}

.win_min {
    background: url(images/ctrl-btn.png) no-repeat 0 0;
}
.ctrlBtn p:hover {
    background-color: #d5e9ff !important;
}
.win_max {
    background: url(images/ctrl-btn.png) no-repeat 0 -30px;
}
.win_close {
    background: url(images/ctrl-btn.png) no-repeat 0 -90px;
}
#win_close:hover {
    background: #cb2c2c url(images/ctrl-btn.png) no-repeat -40px -90px !important;
}

.mainTitle {
    height: 29px;
    font-size: 13px;
    line-height: 30px;
    margin-left: 210px;
    margin-right: 120px;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    top: 0;
}

.onTop {
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
    height: 2px;
    width: 100%;
    position: absolute;
    top: 0;
    z-index: 1;
}

CSS樣式(黑色)shell

/*styleDark.css*/
body, html {
    margin:0;
    padding:0;
    height: 100%; 
    overflow: hidden;
}

.txtBox {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 30px;
    padding-bottom: 50px;
    box-sizing: border-box;
}

.txtEditor {
    width: 100%;
    height: 100%;
    font-size: 16px;
    resize:none;
    outline:none;
    border:0px;
    box-sizing: border-box;
    cursor:auto;
    overflow-y:scroll;
    background-color: #252525;
    color: #c8c8c8;
}

.txtEditor:focus{
    border:0px;
    outline:none;
}

.txtEditor::-webkit-scrollbar {/*滾動條總體樣式*/
    width: 18px;     /*高寬分別對應橫豎滾動條的尺寸*/
    height: 1px;
}
.txtEditor::-webkit-scrollbar-thumb {/*滾動條裏面小方塊*/
    background: #353535;
}
.txtEditor::-webkit-scrollbar-track {/*滾動條裏面軌道*/
    background: #252525;
    border-left: solid 1px #333333;
}

.bottom {
    height: 19px;
    width: 100%;
    font-size: 12px;
    color: #999999;
    text-align: right;
    position: absolute;
    bottom: 0;
    border-top: 1px solid #3c3c3c;
    background-color: #3c3c3c;
}

.txtNum {
    padding-right: 20px;
}


.header {
    -webkit-user-select: none;
    -webkit-app-region: drag;
    height: 29px;
    width: 100%;
    background: #3c3c3c url(images/logo-24.svg) no-repeat 2px 2px;
    border-bottom: 1px solid #3c3c3c;
    position: absolute;
    top: 0;
    z-index:1;
}

/*Menu************************************************/
.menuList {
    width: 210px;
    margin: 0;
    padding: 0;
    float: left;
    display: block;
    position: absolute;
    left: 0;
    top: 0;
}
ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    line-height: 28px;
    font-size: 14px;
    margin-left: 34px;
}
li {
    float: left;
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
}
li p, .dropbtn {
    display: inline-block;
    color: #cccccc;
    text-align: center;
    padding: 1px 6px;
    text-decoration: none;
    margin: 0;
}
li p:hover, .dropdown:hover .dropbtn {
    background-color: #505050;
}
li.dropdown {
    display: inline-block;
}
.dropdown-content {
    display: none;
    position: absolute;
    background-color: #333333;
    min-width: 120px;
    box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.3);
}
.dropdown-content p {
    color: #cccccc;
    padding: 1px 16px;
    margin: 0;
    text-decoration: none;
    display: block;
    text-align: left;
}
.dropdown-content p:hover {
    background-color: #505050;
}
.show {
    display: block;
}

.keyQ{
    float: right;
    padding-left: 10px;
    font-size: 13px;
    color: #999999;
}
.menuS{
    border-bottom: 1px solid #444444;
}

.ctrlBtn {
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
    height: 29px;
    width: 120px;
    display: block;
    position: absolute;
    right: 0;
    top: 0;
}
.ctrlBtn p {
    width: 40px;
    height: 29px;
    float: left;
    margin: 0;
    padding: 0;
    line-height: 29px;
    display: block;
}

.win_min {
    background: url(images/ctrl-btn.png) no-repeat 0 0;
}
.ctrlBtn p:hover {
    background-color: #505050 !important;
}
.win_max {
    background: url(images/ctrl-btn.png) no-repeat 0 -30px;
}
.win_close {
    background: url(images/ctrl-btn.png) no-repeat 0 -90px;
}
#win_close:hover {
    background: #cb2c2c url(images/ctrl-btn.png) no-repeat -40px -90px !important;
}

.mainTitle {
    height: 29px;
    font-size: 13px;
    line-height: 30px;
    margin-left: 210px;
    margin-right: 120px;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    top: 0;
    color: #cccccc;
}

.onTop {
    -webkit-user-select: none;
    -webkit-app-region: no-drag;
    height: 2px;
    width: 100%;
    position: absolute;
    top: 0;
    z-index: 1;
}

GitHub 源碼https://github.com/mongsel/Simple-Notepadjson

相關文章
相關標籤/搜索