前言
一个小小的记事本,除了基本的功能外,如果需要在用户体验方面做的更好,很多小细节需要进行考虑,如关闭前自动保存窗体信息,保存皮肤设置,以及快捷键功能等等。
效果图
主要优化方向
- 模拟主菜单(带快捷键)
- 换肤
- 窗体信息保存
开发思路
默认的主菜单在electron隐藏边框后,依然可以使用快捷键进行操作,在此基础上直接写一个具有点击下拉效果的菜单即可。窗体的长宽、位置、最大化这几个信息获取后利用nodejs的fs模块进行保存,保存为json格式便于读取和调用。换肤功能则采用替换css样式文件。这就是三个主要功能的开发思路,其他小细节在开发中逐步优化。
代码
主进程代码
// 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;
}
});
渲染进程代码
// 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);
}
页面代码
<!-- 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样式(白色)
/*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样式(黑色)
/*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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。