zoukankan      html  css  js  c++  java
  • CloudNotes之桌面客户端篇:笔记撰写样式的支持

    最近在CloudNotes桌面客户端中新增了笔记撰写样式的功能。当用户新建笔记的时候,可以在输入笔记标题的同时,选择笔记撰写样式,由安装包默认提供的样式主要有默认样式(Default)、羊皮纸样式(Leather Paper)以及Word 2013样式(Microsoft Word 2013)。选择笔记样式的时候,还提供了预览功能,用户可以直接预览样式效果:

    image

    当然,为了方便操作,用户可以在设置界面选择默认使用的样式,从而每次新建笔记时,默认使用的样式就会自动被选中,减少用户的操作次数。设置界面中有关默认样式的设定如下:

    image

    现在,我就简单介绍一下,整个样式系统是如何设计实现的。

    样式系统

    与插件系统类似,样式系统也需要读取外部的样式文件,然后把所有读取的样式罗列在设置界面中。因此,在CloudNotes桌面客户端中,样式是可以自定义扩展的。对于如何自定义样式,下一节会作详细介绍。就样式系统本身而言,它有着插件系统相似的行为特征:需要有一个中心组件,对所有的样式进行管理,并在需要的时候,能够通过这个中心组件找到所需的样式定义。于是,在实现样式系统的第一步,我对现有的插件系统进行了重构。

    外部资源管理器(ExternalResourceManager)

    正如上述分析,插件系统和样式系统都需要读取外部文件,因此,我将其抽象成一个外部资源管理器组件,主要负责通过读取外部文件,将文件数据转换成资源(插件或者样式)数据,并对这些数据进行管理。因此,插件管理器(ExtensionManager)和样式管理器(StyleManager)都属于外部资源管理器(ExternalResourceManager)的一种实现,不同之处在于两者对外部文件的读取和使用方式不同,而所处理的数据也不一样。

    image

    首先,在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两个文件,不能将它们放到子目录中,否则桌面客户端将无法读取样式信息。

    以下是一个使用了羊皮纸样式的笔记效果,这在以前版本的桌面客户端中是无法看到的。

    image

  • 相关阅读:
    JAVA_SWT 事件的四种写法
    JAVA静态数据的初始化;
    Centos 安装freesurfer fsl matlab 等软件---转自网络
    ROC和AUC————摘在网络
    转自网络用LIBSVM进行回归预测的粗浅认识————————作者师梦
    fmri格式相关简介————转自网络
    fMRI数据分析处理原理及方法————转自网络
    装机、做系统必备:秒懂MBR和GPT分区表____转载网络
    【转】Thymeleaf自动在URL后加了;jsessionid=的问题
    LeetCode (85): Maximal Rectangle [含84题分析]
  • 原文地址:https://www.cnblogs.com/daxnet/p/4742276.html
Copyright © 2011-2022 走看看