CloudNotes之桌面客户端篇:笔记撰写样式的支持
最近在CloudNotes桌面客户端中新增了笔记撰写样式的功能。当用户新建笔记的时候,可以在输入笔记标题的同时,选择笔记撰写样式,由安装包默认提供的样式主要有默认样式(Default)、羊皮纸样式(Leather Paper)以及Word 2013样式(Microsoft Word 2013)。选择笔记样式的时候,还提供了预览功能,用户可以直接预览样式效果:
当然,为了方便操作,用户可以在设置界面选择默认使用的样式,从而每次新建笔记时,默认使用的样式就会自动被选中,减少用户的操作次数。设置界面中有关默认样式的设定如下:
现在,我就简单介绍一下,整个样式系统是如何设计实现的。
样式系统
与插件系统类似,样式系统也需要读取外部的样式文件,然后把所有读取的样式罗列在设置界面中。因此,在CloudNotes桌面客户端中,样式是可以自定义扩展的。对于如何自定义样式,下一节会作详细介绍。就样式系统本身而言,它有着插件系统相似的行为特征:需要有一个中心组件,对所有的样式进行管理,并在需要的时候,能够通过这个中心组件找到所需的样式定义。于是,在实现样式系统的第一步,我对现有的插件系统进行了重构。
外部资源管理器(ExternalResourceManager)
正如上述分析,插件系统和样式系统都需要读取外部文件,因此,我将其抽象成一个外部资源管理器组件,主要负责通过读取外部文件,将文件数据转换成资源(插件或者样式)数据,并对这些数据进行管理。因此,插件管理器(ExtensionManager)和样式管理器(StyleManager)都属于外部资源管理器(ExternalResourceManager)的一种实现,不同之处在于两者对外部文件的读取和使用方式不同,而所处理的数据也不一样。
首先,在ExternalResourceManager抽象类的Load方法中,会根据构造函数传入的路径,去搜索所有匹配搜索条件的文件,并通过LoadResources抽象方法,从每个符合搜索条件的文件中载入资源:
/// <summary>
/// Loads the resources into the current manager.
/// </summary>
public void Load()
{
if (Directory.Exists(this.path))
{
var resourceFiles = Directory.EnumerateFiles(this.path, this.searchPattern, SearchOption.AllDirectories); foreach (var resourceFile in resourceFiles)
{
try
{
var res = LoadResources(resourceFile);
if (res != null && res.Any())
{
foreach (var resource in res)
{
this.resources.Add(resource.ID, resource);
}
}
}
catch
{
}
}
}
}
然后,ExtensionManager在LoadResources方法的实现中,通过Assembly.LoadFrom调用,将Assembly读入内存,同时使用Activator.CreateInstance创建Extension对象,然后返回给基类进行管理:
protected override IEnumerable<Extension> LoadResources(string fileName)
{
var assembly = Assembly.LoadFrom(fileName);
var result = new List<Extension>();
foreach (var type in assembly.GetExportedTypes())
{
if (type.IsDefined(typeof (ExtensionAttribute)) &&
type.IsSubclassOf(typeof (Extension)))
{
try
{
var extensionLoaded = (Extension) Activator.CreateInstance(type);
this.OnResourceLoaded(extensionLoaded);
result.Add(extensionLoaded);
}
catch
{
}
}
}
return result;
}
而在StyleManager的LoadResources方法的实现中,则将传入的文件以zip解压缩,从中读取metadata.json文件以获得样式的基本定义信息,并从中读取style.css文件以获得样式的详细内容,之后,会使用Json库对metadata.json反序列化,并得到Style对象。最后,再将Style对象返回给基类进行管理:
protected override IEnumerable<Style> LoadResources(string fileName)
{
try
{
var extractedContent = new Dictionary<string, string>();
var zipFile = new ZipFile(fileName);
foreach (ZipEntry entry in zipFile)
{
if (!entry.IsFile ||
(string.Compare(entry.Name, MetadataFileName, StringComparison.InvariantCultureIgnoreCase) != 0 &&
string.Compare(entry.Name, StyleFileName, StringComparison.InvariantCultureIgnoreCase) != 0))
continue; var buffer = new byte[4000];
var entryStream = zipFile.GetInputStream(entry);
using (MemoryStream memoryStream = new MemoryStream())
{
StreamUtils.Copy(entryStream, memoryStream, buffer);
extractedContent[entry.Name] = Encoding.ASCII.GetString(memoryStream.ToArray());
}
}
if (extractedContent.ContainsKey(MetadataFileName) &&
extractedContent.ContainsKey(StyleFileName))
{
var style = JsonConvert.DeserializeObject<Style>(extractedContent[MetadataFileName]);
if (style.ID == Guid.Empty)
return null;
style.Content = extractedContent[StyleFileName];
return new[] { style };
}
}
catch
{
} return null;
}
就样式数据而言,它包含ID、样式名称、样式描述、创建日期、作者以及样式代码(定义在style.css文件中),Style对象保存了这些数据,并被用于设置页面以及新笔记创建等场景中。
样式的应用原理
其实,样式的应用原理非常简单,就是在创建新笔记时,使用样式文件中的style.css的内容去替换空Html文档的head下的style标签部分的占位符即可。在CloudNotes中,空Html文档的定义如下:
<html>
<head>
<style>
$style$
</style>
</head>
<body> </body>
</html>
说它是空Html文档,并不是说它是一个空字符串,而是因为body部分是没有任何内容的。假设style.css中的样式内容如下:
body {
color: red;
}
那么,最终生成的笔记内容就是:
<html>
<head>
<style>
body {
color: red;
}
</style>
</head>
<body> </body>
</html>
值得一提的是,在CloudNotes桌面客户端中,样式系统的实现还是相对简单的,因此,除了metadata.json和style.css两个文件外,其它的文件都会被忽略。如果需要使用外部文件作为样式的资源,比如希望能够指定背景图片,那么就应该将图片数据转成base64,然后使用data:image的方式指定图片的URL。
自定义样式
CloudNotes桌面客户端笔记撰写样式的自定义也是非常简单的,样式文件本身就是一个以style为扩展名的zip压缩文件。要自定义样式,首先新建一个metadata.json文件,该文件内容如下:
{
"ID": "F04F9079-EC4E-4739-93C5-473607BEA79E", // 样式的Guid
"Name": "Default", // 样式名称
"Description": "CloudNotes Default Style", // 样式描述
"Author": "Sunny Chen", // 样式作者
"CreationDate": "8/8/2015" // 样式创建日期
}
然后在metadata.json相同的路径下,新建一个style.css文件,该文件的内容相信大家都知道是什么,就是css样式。两个文件准备好以后,将它们压缩成zip文件,然后将扩展名改为style,并拷贝到CloudNotes桌面客户端安装路径的Styles子目录下即可。在重启CloudNotes桌面客户端之后,就可以在新建笔记和设置界面中看到并选择这个自定义样式了。说明一点,zip文件中仅能包含metadata.json和style.css两个文件,不能将它们放到子目录中,否则桌面客户端将无法读取样式信息。
以下是一个使用了羊皮纸样式的笔记效果,这在以前版本的桌面客户端中是无法看到的。
CloudNotes之桌面客户端篇:笔记撰写样式的支持的更多相关文章
- CloudNotes之桌面客户端篇:插件系统的实现
[CloudNotes版本更新历史与各版本下载地址请点击此处] [CloudNotes中文系列文章汇总列表请点击此处] [查看CloudNotes源代码请点击此处] 有时候,同一个名词,针对不同的人群 ...
- CloudNotes之桌面客户端篇:增强的笔记列表
今天,我发布了CloudNotes的一个更新版本:1.0.5484.36793.这个版本与1.0.5472.20097不同的是,它拥有增强的笔记列表,与之前单调的列表系统相比,新的笔记列表不仅可以显示 ...
- CloudNotes:一个云端个人笔记系统
很长时间没有更新博客了,一直在忙着工作和生活琐事,虽然偶尔也有闲暇之时,但短短的几个小时空闲又未必能够静下心来.最近一个多月突发奇想,将自己在近一年前做的一个自己用的云端个人笔记系统重构美化了一下,增 ...
- C#Windows Service服务程序的安装/卸载、启动/停止 桌面客户端管理程序设计
C#Windows Service服务程序的安装/卸载.启动/停止 桌面客户端管理程序设计 关于Windows Service程序的安装与卸载如果每次使用命令行操作,那简直要奔溃了,太麻烦而且还容易出 ...
- "xaml+cs"桌面客户端跨平台初体验
"Xaml+C#"桌面客户端跨平台初体验 前言 随着 .Net 5的到来,微软在 .Net 跨平台路上又开始了一个更高的起点.回顾.Net Core近几年的成果,可谓是让.Ne ...
- [原创]用windows7连接windows2003的终端服务器时,出现"由于这台计算机没有远程桌面客户端访问许可证,远程会话被中断"的问题
用windows7连接windows2003的终端服务器时,出现"由于这台计算机没有远程桌面客户端访问许可证,远程会话被中断"的问题,原因是终端服务器授权方式设置为了"每 ...
- Atitit 全屏模式的cs桌面客户端软件gui h5解决方案 Kiosk模式
Atitit 全屏模式的cs桌面客户端软件gui h5解决方案 Kiosk模式 1.1. Kiosk Software广泛用于公共电脑或者嵌入系统,最常用的就是ATM机.自动服务机之类的系统了.,1 ...
- [转]RamDisk导致远程桌面客户端无法启动问题
在一次重启系统后发现无法运行远程桌面客户端,运行后进行连接即报错. 查看日志有AppCrash错误: 错误应用程序名称: mstsc.exe,版本: 6.1.7600.16385,时间戳: 0x4a5 ...
- Windows苹果安卓手机远程桌面客户端推荐
适用于:Windows 10.Windows 8.1.Windows Server 2012 R2.Windows Server 2016 最近公司电脑从Windows7升级到了Windows10,然 ...
随机推荐
- 【原】Github+Hexo+NextT搭建个人博客
摘要 GitHub 是一个开源项目的托管网站,相信很多人都听过.在上面有很多高质量的项目代码,我们也可以把自己的项目代码托管到GitHub,与朋友们共享交流.GitHub Pages 是Github为 ...
- junit4进行单元测试
一.前言 提供服务的时候,为了保证服务的正确性,有时候需要编写测试类验证其正确性和可用性.以前的做法都是自己简单写一个控制层,然后在控制层里调用服务并测试,这样做虽然能够达到测试的目的,但是太不专业了 ...
- 和 Thrift 的一场美丽邂逅
一. 与 Thrift 的初识 也许大多数人接触 Thrift 是从序列化开始的.每次搜索 “java序列化” + “方式”.“对比” 或 “性能” 等关键字时,搜索引擎总是会返回一大堆有关各种序列化 ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密
string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...
- 非关系型数据库(NoSql)
最近了解了一点非关系型数据库,刚刚接触,觉得这是一个很好的方向,对于大数据 方面的处理,非关系型数据库能起到至关重要的地位.这里我主要是整理了一些前辈的经验,仅供参考. 关系型数据库的特点 1.关系型 ...
- Mysql命令大全
格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root -p,回车后提示你输 ...
- node中子进程同步输出
管道 通过"child_process"模块fork出来的子进程都是返回一个ChildProcess对象实例,ChildProcess类比较特殊无法手动创建该对象实例,只能使用fo ...
- C++随笔:.NET CoreCLR之corleCLR核心探索之coreconsole(1)
一看这个标题,是不去取名有点绕呢?或者是,还有些问题?报告LZ...你的标题取得有问题,是个病句!↖(^ω^)↗!!!先不要急,其实我今天带给大家的就是CoreCLR中的coreclr.其中它是在名字 ...
- 海康网络摄像机YV12转换为BGR,由opencv Mat显示 (转)
我使用的是海康DS-2CD852MF-E, 200万,网络摄像机,已经比较老了,不过SDK在海康官网下载的,开发流程都差不多. 海康摄像机回调解码后的视频数据格式为YV12,顺便说一下YV12的数据格 ...