├── README.md └── docs ├── amWiki ├── css │ ├── amWiki.css │ ├── lhjs.github-gist.css │ └── markdownbody.github.css ├── images │ ├── icons.svg │ ├── logo.png │ └── menubar_bg.png └── js │ ├── amWiki.docs.js │ ├── amWiki.js │ ├── amWiki.scrollbar.js │ ├── amWiki.search.js │ ├── amWiki.search.worker.js │ ├── amWiki.storage.js │ ├── amWiki.testing.js │ ├── amWiki.tools.js │ ├── flowchart.min.js │ ├── gbk.js │ ├── highlight.min.js │ ├── jquery-compat-3.1.0.min.js │ ├── marked.min.js │ └── raphael-min.js ├── assets ├── 000 │ ├── 000-1495808550000.gif │ ├── 000-1495808586000.gif │ ├── 000-1495808595000.gif │ ├── 000-1495808604000.gif │ ├── 000-1495808615000.gif │ └── 000-1495808625000.gif ├── 001 │ ├── 02-1495810061000.png │ ├── 02-1495810090000.png │ └── 02-1495810116000.png ├── 002 │ ├── 001-1495812823000.png │ ├── 002-1495813468000.png │ ├── 003-1495814105000.png │ ├── 003-1495814114000.png │ ├── 003-1495814123000.png │ ├── 003-1495814132000.png │ ├── 003-1495814143000.png │ ├── 003-1495814149000.png │ ├── 003-1495814157000.png │ ├── 004-1495814809000.png │ ├── 004-1495814948000.png │ ├── 004-1495815060000.png │ ├── 006-1495895049000.png │ ├── 006-1495895175000.png │ ├── 006-1495895193000.png │ ├── 006-1495895246000.png │ ├── 007-1495895465000.png │ ├── 007-1495895479000.png │ ├── 007-1495895495000.png │ ├── 007-1495895510000.png │ ├── 007-1495895544000.png │ ├── 007-1495895981000.png │ ├── 007-1495896031000.png │ ├── 007-1495896060000.png │ ├── 007-1495896085000.png │ ├── 007-1495896102000.png │ ├── 009-1495897020000.png │ ├── 010-1495897765000.png │ ├── 011-1495898542000.png │ ├── 013-1495899309000.png │ ├── 020-1496319441000.gif │ ├── 023-1496320185000.gif │ ├── 025-1496323861000.png │ ├── 026-1496324134000.gif │ ├── 026-1496324144000.gif │ ├── 029-1496325573000.gif │ ├── 030-1496326622000.png │ └── 030-1496326635000.png ├── 003 │ ├── 02-1497534561000.png │ ├── 02-1497534594000.png │ ├── 10-1497542012000.png │ ├── 11-1497542079000.gif │ └── 12-1497542440000.png └── 004 │ ├── 01-1497530792000.png │ ├── 01-1497530811000.png │ ├── 01-1497530852000.gif │ ├── 01-1497530889000.png │ ├── 01-1497530922000.png │ ├── 01-1497530969000.png │ ├── 01-1497530978000.png │ ├── 01-1497530986000.png │ ├── 01-1497530995000.png │ ├── 01-1497531008000.png │ ├── 01-1497531033000.png │ ├── 01-1497531045000.png │ ├── 01-1497531067000.png │ ├── 01-1497531086000.png │ ├── 01-1497531116000.png │ ├── 01-1497531125000.png │ ├── 01-1497531134000.png │ ├── 01-1497531160000.png │ ├── 01-1497531167000.png │ ├── 01-1497531173000.png │ ├── 01-1497531194000.png │ ├── 01-1497531201000.png │ ├── 01-1497531209000.png │ ├── 01-1497531217000.png │ ├── 01-1497531227000.png │ ├── 01-1497531259000.png │ ├── 01-1497531285000.png │ ├── 01-1497531307000.png │ ├── 01-1497531330000.png │ ├── 01-1497531338000.png │ ├── 01-1497531346000.png │ ├── 01-1497531362000.png │ ├── 01-1497531379000.png │ ├── 01-1497531387000.png │ ├── 01-1497531472000.png │ ├── 01-1497531526000.png │ ├── 01-1497531543000.png │ ├── 01-1497531552000.png │ ├── 01-1497531563000.png │ ├── 01-1497531571000.png │ ├── 01-1497531590000.png │ ├── 01-1497531605000.png │ ├── 01-1497531612000.png │ ├── 01-1497531619000.png │ ├── 01-1497531774000.png │ ├── 01-1497531795000.png │ ├── 02-1497534785000.gif │ ├── 03-1497540772000.gif │ ├── 04-1497540970000.gif │ ├── 04-1497541302000.gif │ ├── 04-1497541321000.gif │ └── 04-1497541329000.gif ├── config.json ├── index.html └── library ├── $navigation.md ├── 001-SOUI 概述 ├── 01-SOUI特点.md └── 02-SOUI亮点.md ├── 002-使用教程 ├── 001-第一篇:SOUI是什么?.md ├── 002-第二篇:SOUI源码的获取及编译.md ├── 003-第三篇:用SOUI能做什么?.md ├── 004-第四篇:SOUI资源文件组织.md ├── 005-第五篇:在SOUI中使用XML布局属性指引.md ├── 006-第六篇:在SOUI中用九宫格拉伸方式显示一个图片资源.md ├── 007-第七篇:创建一个SOUI的Hello World.md ├── 008-第八篇:SOUI中控件事件的响应.md ├── 009-第九篇:在SOUI中使用多语言翻译.md ├── 010-第十篇:扩展SOUI的控件及绘图对象.md ├── 011-第十一篇:SOUI系统资源管理.md ├── 012-第十二篇:SOUI的utilities模块为什么要用DLL编译?.md ├── 013-第十三篇:在SOUI中使用有窗口句柄的子窗口.md ├── 014-第十四篇:在SOUI中使用定时器.md ├── 015-第十五篇:在SOUI中消息通讯.md ├── 016-第十六篇:SWindow的布局属性pos2type及offset.md ├── 017-第十七篇:使用窗口的cache属性加速SOUI的渲染.md ├── 018-第十八篇:在SOUI中实现PreTranslateMessage.md ├── 019-第十九篇:提高SOUI应用程序渲染性能的三种武器.md ├── 020-第二十篇:在SOUI中使用分层窗口.md ├── 021-第二十一篇:SOUI中的控件注册机制.md ├── 022-第二十二篇:在SOUI中使用代码向窗口中插入子窗口.md ├── 023-第二十三篇:在SOUI中使用LUA脚本开发界面.md ├── 024-第二十四篇:导出SOUI对象到LUA脚本.md ├── 025-第二十五篇:在SOUI中做事件分发处理.md ├── 026-第二十六篇:两个SOUI新控件 ---- SListView和SComboView.md ├── 027-第二十七篇:SOUI中控件属性查询方法.md ├── 028-第二十八篇:SOUI中自定义控件开发过程.md ├── 029-第二十九篇:使用SOUI的SMCListView控件.md ├── 030-第三十篇:SOUI模块结构图及SOUI框架图.md ├── 031-第三十一篇:SOUI布局之相对于特定兄弟窗口.md ├── 032-第三十二篇:在SOUI2.0中像android一样使用资源.md ├── 033-第三十三篇:使用uiresImporter生成uires.idx及skin.xml.md └── 034-第三十四篇:在SOUI中使用异步通知.md ├── 003-技术分析 ├── 01-为GDI函数增加透明度处理.md ├── 02-实现一种快速查找Richedit中可见区域内OLE对象的方法.md ├── 03-一种快速刷新richedit中内嵌动画的方法的实现.md ├── 04-为什么在soui中加载JPG文件失败.md ├── 05-一种高效的可变行高列表行定位算法 - 副本.md ├── 06-在SOUI中非半透明窗口如何实现圆角窗口?.md ├── 07-SOUI与WTL.md ├── 08-不注册COM在Richedit中使OLE支持复制粘贴.md ├── 09-在SOUI中使用线性布局.md ├── 10-在SOUI中使用窗口自適應大小.md ├── 11-在SOUI中使用动态多语言切换.md └── 12-在SOUI中支持高分屏显示.md ├── 004-案例演示 ├── 01-基于SOUI开发的应用展示.md ├── 02-使用SOUI开发的界面集锦.md ├── 03-SOUI中做的一个磁力吸附效果.md └── 04-SOUI Editor使用教程.md └── 首页.md /README.md: -------------------------------------------------------------------------------- 1 | # 欢迎访问SOUIWiki ヾ(o´∀`o)ノ 2 | 3 | ### 用C++做产品最痛苦的是什么?肯定是做UI。 4 | 5 | ### SOUI的使命就是把痛苦的UI变化成快乐的UI。 6 | 7 | ### 什么?UI还能快乐?脑子进水了吗? 8 | 9 | ### 当你看完这个系统教程的时候相信你面对UI至少不会再痛苦。你可以对于PM说,UI?Just SO SO! 10 | 11 | ### 闲话少说,进入正题。 12 | 13 | #### 戳[**我**](https://soui2.github.io/SOUIWiki/)!!! -------------------------------------------------------------------------------- /docs/amWiki/css/lhjs.github-gist.css: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub Gist Theme 3 | * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | background: white; 9 | padding: 0.5em; 10 | color: #333333; 11 | overflow-x: auto; 12 | } 13 | 14 | .hljs-comment, 15 | .hljs-meta { 16 | color: #969896; 17 | } 18 | 19 | .hljs-string, 20 | .hljs-variable, 21 | .hljs-template-variable, 22 | .hljs-strong, 23 | .hljs-emphasis, 24 | .hljs-quote { 25 | color: #df5000; 26 | } 27 | 28 | .hljs-keyword, 29 | .hljs-selector-tag, 30 | .hljs-type { 31 | color: #a71d5d; 32 | } 33 | 34 | .hljs-literal, 35 | .hljs-symbol, 36 | .hljs-bullet, 37 | .hljs-attribute { 38 | color: #0086b3; 39 | } 40 | 41 | .hljs-section, 42 | .hljs-name { 43 | color: #63a35c; 44 | } 45 | 46 | .hljs-tag { 47 | color: #333333; 48 | } 49 | 50 | .hljs-title, 51 | .hljs-attr, 52 | .hljs-selector-id, 53 | .hljs-selector-class, 54 | .hljs-selector-attr, 55 | .hljs-selector-pseudo { 56 | color: #795da3; 57 | } 58 | 59 | .hljs-addition { 60 | color: #55a532; 61 | background-color: #eaffea; 62 | } 63 | 64 | .hljs-deletion { 65 | color: #bd2c00; 66 | background-color: #ffecec; 67 | } 68 | 69 | .hljs-link { 70 | text-decoration: underline; 71 | } 72 | -------------------------------------------------------------------------------- /docs/amWiki/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/amWiki/images/logo.png -------------------------------------------------------------------------------- /docs/amWiki/images/menubar_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/amWiki/images/menubar_bg.png -------------------------------------------------------------------------------- /docs/amWiki/js/amWiki.scrollbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc amWiki Web端·滚动条 3 | * @author Tevin 4 | */ 5 | 6 | ; 7 | (function (win, $) { 8 | 9 | 'use strict'; 10 | 11 | //定时检测内容高度作为补充 12 | var list = []; 13 | setInterval(function () { 14 | for (var i = 0, item; item = list[i]; i++) { 15 | item.checkHeight(); 16 | } 17 | }, 500); 18 | 19 | var Scroller = function (_this) { 20 | this.$e = { 21 | container: $(_this), //主容器 22 | inner: null, //正文内容 23 | bar: null, //滚动条外层 24 | box: null, //滚动条 25 | slider: null //滑块 26 | }; 27 | this.data = { 28 | contentH: 0, //内容高度 29 | containerH: 0, //容器高度 30 | containerHLast: 0, //上次容器高度 31 | barH: 0, //滚动条高度 32 | sliderH: 0 //滑块高度 33 | }; 34 | this._init(); 35 | }; 36 | 37 | Scroller.prototype._init = function () { 38 | var that = this; 39 | this.$e.container.append('
'); 40 | this.$e.bar = this.$e.container.find('.scrollbar'); 41 | this.$e.box = this.$e.bar.children('div'); 42 | this.$e.slider = this.$e.bar.find('i'); 43 | this.$e.inner = this.$e.container.children('.scroller-inner'); 44 | this._onWinResize(); 45 | $(window).on('resize', function () { 46 | that._onWinResize(); 47 | that.checkHeight(); 48 | }); 49 | this.$e.container.on('scrollbar', function () { 50 | that.checkHeight(); 51 | }); 52 | this.$e.inner.on({ 53 | 'click': function () { 54 | that.checkHeight(); 55 | }, 56 | 'scroll': function () { 57 | that._reScroll(); 58 | } 59 | }); 60 | this.checkHeight(); 61 | this._bindAction(); 62 | }; 63 | 64 | //滑块操作 65 | Scroller.prototype._bindAction = function () { 66 | var that = this; 67 | var onDrag = false; 68 | var _y = 0, 69 | top = 0; 70 | this.$e.body = $('body'); 71 | this.$e.box.on({ 72 | 'mousedown': function (e) { 73 | onDrag = true; 74 | that.$e.body.attr('onselectstart', 'return false'); 75 | that.$e.bar.addClass('active'); 76 | _y = e.pageY; 77 | top = parseFloat(that.$e.slider.css('top')); 78 | if (that.$e.bar.parents('#main').length > 0) { 79 | that.$e.body.children('aside').hide(); 80 | } 81 | } 82 | }); 83 | $(document).on({ 84 | 'mousemove': function (e) { 85 | if (onDrag) { 86 | that.scrollTo(top + e.pageY - _y); 87 | } 88 | }, 89 | 'mouseup': function () { 90 | onDrag = false; 91 | that.$e.body.removeAttr('onselectstart', 'return false'); 92 | that.$e.bar.removeClass('active'); 93 | that.$e.body.children('aside').show(); 94 | } 95 | }); 96 | }; 97 | 98 | Scroller.prototype._onWinResize = function () { 99 | if (isMobi()) { 100 | this.$e.inner.removeAttr('style').removeClass('on'); 101 | } else { 102 | this.$e.inner.css({ 103 | width: this.$e.container.width() + 30, 104 | paddingRight: 13 105 | }).addClass('on'); 106 | } 107 | }; 108 | 109 | //重设滑块大小 110 | Scroller.prototype._resize = function () { 111 | var that = this; 112 | this.data.containerH = this.$e.inner.height(); 113 | //当内容高度小于等于容器时,不显示滚动条 114 | if (this.data.contentH <= this.data.containerH) { 115 | this.data.contentH = this.data.containerH; 116 | this.$e.bar.addClass('off'); 117 | } else { 118 | this.$e.bar.removeClass('off'); 119 | } 120 | this.data.barH = this.$e.box.height(); 121 | this.data.sliderH = this.data.containerH / that.data.contentH * this.data.barH; 122 | this.$e.slider.height(this.data.sliderH); 123 | }; 124 | 125 | //重设滑块顶部距离 126 | Scroller.prototype._reScroll = function () { 127 | this.$e.slider.css('top', this.$e.inner.scrollTop() / this.data.contentH * this.data.barH); 128 | }; 129 | 130 | //滚动多少距离 131 | Scroller.prototype.scrollTo = function (num) { 132 | var barTop = num; 133 | barTop = barTop < 0 ? 0 : barTop; 134 | barTop = barTop + this.data.sliderH > this.data.barH ? this.data.barH - this.data.sliderH : barTop; 135 | this.$e.slider.css('top', barTop); 136 | this.$e.inner.scrollTop(barTop / this.data.barH * this.data.contentH); 137 | }; 138 | 139 | //检查高度 140 | Scroller.prototype.checkHeight = function () { 141 | var that = this; 142 | this.data.contentH = 0; 143 | this.$e.inner.children('.scroller-content').each(function () { 144 | that.data.contentH += $(this).outerHeight(); 145 | }); 146 | //如果高度未改变不进行操作 147 | if (this.data.contentH == this.data.contentHLast) { 148 | return; 149 | } else { 150 | this.data.contentHLast = this.data.contentH; 151 | } 152 | this._resize(); 153 | this._reScroll(); 154 | }; 155 | 156 | //方法绑定 157 | $.extend($.fn, { 158 | scrollbar: function () { 159 | return this.each(function () { 160 | list.push(new Scroller(this)); 161 | }); 162 | } 163 | }); 164 | 165 | })(window, jQuery); -------------------------------------------------------------------------------- /docs/amWiki/js/amWiki.search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc amWiki Web端·全库搜索 3 | * @author Tevin 4 | */ 5 | 6 | ; 7 | (function (win) { 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * @class 创建全局搜索 13 | */ 14 | var Search = function (_storage) { 15 | this._storage = _storage; 16 | this.elm = { 17 | //显示搜索面板按钮 18 | $searchShow: $('#searchShow'), 19 | //更新全部缓存按钮 20 | $searchUpdate: this._storage.elm.$searchUpdate, 21 | //搜索面板 22 | $searchBox: $('#searchBox'), 23 | //搜索结果列表 24 | $results: $('#results'), 25 | //搜索结果信息提示 26 | $resultMsg: $('#resultMsg'), 27 | //搜素结果显示更多 28 | $resultMore: $('#resultMore'), 29 | //搜索按钮 30 | $search: $('#search'), 31 | //搜索文本 32 | $searchText: $('#searchText') 33 | }; 34 | this._data = { 35 | //搜素结果 36 | result: [], 37 | //单条结果的模板 38 | template: $('#template\\:searchResult').text(), 39 | //每页结果数 40 | pageSize: 15, 41 | //当前页码 42 | pagination: 0 43 | }; 44 | this._bindCtrl(); 45 | //用户执行重建缓存的回调 46 | this.onNeedRebuildStorage = null; 47 | }; 48 | 49 | //初始化 50 | Search.prototype._bindCtrl = function () { 51 | var that = this; 52 | //展开折叠搜索面板 53 | this.elm.$searchShow.on('click', function () { 54 | if (that.elm.$searchBox.hasClass('on')) { 55 | that.displayBox('off'); 56 | } else { 57 | that.displayBox('on', function () { 58 | resetResHeight(); 59 | }); 60 | } 61 | }); 62 | //设置结果区域高度 63 | var resetResHeight = function () { 64 | var hOut = that.elm.$searchBox.height(); 65 | var dt = that.elm.$results.offset().top - that.elm.$searchUpdate.offset().top; 66 | that.elm.$results.height(hOut - dt); 67 | }; 68 | $(win).on('resize', function () { 69 | if (that.elm.$searchBox.hasClass('on')) { 70 | resetResHeight(); 71 | } 72 | }); 73 | //重建缓存 74 | this.elm.$searchUpdate.on('click', function () { 75 | //开启重建缓存时,如果存在搜索子进程,则干掉子进程 76 | if (that._worker) { 77 | that._worker.terminate(); 78 | that._worker = null; 79 | that.elm.$resultMsg.hide(); 80 | } 81 | that.elm.$search.prop('disabled', true); 82 | that.elm.$searchUpdate.prop('disabled', true); 83 | that.onNeedRebuildStorage(function () { 84 | that.elm.$search.prop('disabled', false); 85 | that.elm.$searchUpdate.val('请勿频繁使用'); 86 | }); 87 | }); 88 | //更新全部缓存按钮使用的时间限制:一小时内不允许重复使用 89 | var lastBuild = this._storage.getLastBuildTs(); 90 | if (lastBuild) { 91 | var lave = Date.now() - lastBuild; 92 | if (lave < 60 * 60 * 1000) { 93 | this.elm.$searchUpdate.prop('disabled', true).val('请勿频繁使用'); 94 | } 95 | } 96 | //点击搜索 97 | this.elm.$search.on('click', function () { 98 | that._search(); 99 | }); 100 | this.elm.$searchText.on('keyup', function (e) { 101 | if (e.keyCode == 13) { 102 | that._search(); 103 | } 104 | }); 105 | //结果翻页 106 | this.elm.$resultMore.on('click', function () { 107 | that._nextResultPage(); 108 | }); 109 | }; 110 | 111 | //显示隐藏搜索面板 112 | Search.prototype.displayBox = function (type, callback) { 113 | var that = this; 114 | var $box = this.elm.$searchBox; 115 | if (type == 'on' && !$box.hasClass('on')) { 116 | $box 117 | .addClass('on') 118 | .css({ 119 | 'display': 'block', 120 | 'width': '0', 121 | 'opacity': 0 122 | }) 123 | .animate({ 124 | 'width': '100%', 125 | 'opacity': 1 126 | }, 300, 'swing', function () { 127 | callback && callback(); 128 | }); 129 | } else if (type == 'off' && $box.hasClass('on')) { 130 | $box 131 | .removeClass('on') 132 | .animate({ 133 | 'width': '30%', 134 | 'opacity': 0 135 | }, 200, 'swing', function () { 136 | $box.removeAttr('style'); 137 | callback && callback(); 138 | }); 139 | } 140 | }; 141 | 142 | //启动搜素 143 | Search.prototype._search = function () { 144 | var that = this; 145 | if (this.elm.$searchText.val() == '') { 146 | this.elm.$searchText.focus(); 147 | return; 148 | } 149 | var words = this.elm.$searchText.val(); 150 | if (typeof win.Worker !== "undefined") { 151 | //开启一次新搜索时,如果存在搜索子进程,则干掉子进程 152 | if (this._worker) { 153 | this._worker.terminate(); 154 | this._worker = null; 155 | this.elm.$resultMsg.hide(); 156 | } 157 | this.elm.$resultMsg.show().text('创建搜索中...'); 158 | //创建子进程 159 | this._worker = new win.Worker('amWiki/js/amWiki.search.worker.js'); 160 | //收到子进程搜素消息 161 | this._worker.onmessage = function (event) { 162 | var data = event.data; 163 | //加载成功后发送文档数据 164 | if (data.type == 'loaded') { 165 | that._worker.postMessage({type: 'docs', docs: that._storage.getAllDocs()}); 166 | } 167 | //文档预处理完成后开始搜索 168 | else if (data.type == 'ready') { 169 | that.elm.$resultMsg.show().html('正在搜索,请稍后...'); 170 | that._worker.postMessage({type: 'search', words: words}); 171 | } 172 | //搜索结果排行 173 | else if (data.type == 'result') { 174 | that._data.result = data.result; 175 | that._showResultList(); 176 | that._worker.terminate(); 177 | that._worker = null; 178 | } 179 | }; 180 | //子进程出错 181 | this._worker.onerror = function (e) { 182 | console.error(e); 183 | that._worker.terminate(); 184 | that._worker = null; 185 | }; 186 | } else { 187 | this.elm.$resultMsg.show().text('Sorry,您的浏览器不支持搜索!'); 188 | this.elm.$search.prop('disabled', true); 189 | } 190 | 191 | }; 192 | 193 | //显示结果列表 194 | Search.prototype._showResultList = function () { 195 | this.elm.$results.children('ul').children().remove(); 196 | this.elm.$resultMsg.hide(); 197 | this._data.pagination = 0; 198 | this._nextResultPage(); 199 | }; 200 | 201 | //显示结果列表下一页 202 | Search.prototype._nextResultPage = function () { 203 | var html = ''; 204 | var count = 0; 205 | for (var i = this._data.pagination * this._data.pageSize, item; item = this._data.result[i]; i++) { 206 | html += this._renderRankItem(this._data.template, item); 207 | //超过页码跳出 208 | if (++count >= this._data.pageSize) { 209 | break; 210 | } 211 | } 212 | this.elm.$results.children('ul').append(html); 213 | this._data.pagination++; 214 | //如果还有结果没显示完,显示显示更多按钮 215 | if (this._data.pagination * this._data.pageSize >= this._data.result.length) { 216 | this.elm.$resultMore.hide(); 217 | } else { 218 | this.elm.$resultMore.show(); 219 | } 220 | }; 221 | 222 | //渲染单条模板 223 | Search.prototype._renderRankItem = function (template, data) { 224 | var tmpl = template; 225 | data.time = win.tools.formatTime(data.timestamp); 226 | delete data.timestamp; 227 | for (var p in data) { 228 | if (data.hasOwnProperty(p)) { 229 | tmpl = tmpl.replace(new RegExp('{{' + p + '}}', 'g'), data[p]); 230 | } 231 | } 232 | return tmpl; 233 | }; 234 | 235 | return win.AWSearch = Search; 236 | 237 | })(window); -------------------------------------------------------------------------------- /docs/amWiki/js/amWiki.search.worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc amWiki Web端·搜索子进程 3 | * @author Tevin 4 | */ 5 | 6 | 7 | (function (self) { 8 | 9 | 'use strict'; 10 | 11 | //计算器 12 | var searcher = (function () { 13 | 14 | var Searcher = function () { 15 | //文档存储 16 | this._documents = null; 17 | //搜索处理结果 18 | this._processing = {}; 19 | //设置 20 | this._data = { 21 | //标题命中得分 22 | titleScore: 100, 23 | //接口地址命中得分 24 | apiScore: 50, 25 | //单次内容命中得分 26 | textScore: 5 27 | } 28 | }; 29 | 30 | //初始文档 31 | Searcher.prototype.initDocs = function (docs) { 32 | this._documents = docs; 33 | for (var id in this._documents) { 34 | if (this._documents.hasOwnProperty(id)) { 35 | this._preDoc(this._documents[id]); 36 | } 37 | } 38 | }; 39 | 40 | //文档预处理 41 | Searcher.prototype._preDoc = function (doc) { 42 | doc.content = doc.content 43 | .replace(/^\s+|\s+$/g, '') 44 | //分离h1标题 45 | .replace(/^#\s?(.*?)[\r|\n]/, function (match, s1) { 46 | doc.title = s1; 47 | return ''; 48 | }) 49 | //分离测试文档请求地址 50 | .replace(/([^#]#{3} *请求地址[\n\r]{1,4})([-\w:\/\.]+?)[\n\r]{1,}(#{3} *请求类型[\s\S]+?#{3} *请求参数)/, 51 | function (match, s1, s2, s3) { 52 | doc.api = s2; 53 | return s1 + s3; 54 | }) 55 | //清除 Markdown 标题标记 56 | .replace(/#{1,6}(.*?)#{0,6}\s*[\r\n]/g, '$1') 57 | //清除 Markdown 强调斜体删除线标记 58 | .replace(/[_\*~]{1,2}(.*?)[_\*~]{1,2}/g, '$1') 59 | //直接删除 Markdown 图片 60 | .replace(/!\[.*?]\(.*?\)/g, '') 61 | //清除 Markdown 链接标记,还原为显示文本 62 | .replace(/\[(.*?)]\(.*?\)/g, '$1') 63 | //清除 Markdown 代码段标记 64 | .replace(/`{3}.*?[\n\r]([\s\S]*?)`{3}/g, '$1') 65 | //清除 Markdown 代码标记 66 | .replace(/`(.*?)`/g, '$1') 67 | //直接删除所有 html 标签 68 | .replace(/<.+?>/g, '') 69 | //清除 Markdown 引用标记 70 | .replace(/> *(.+?)[n\r]/g, '$1') 71 | //清除 Markdown 分割线标记 72 | .replace(/-{3,} *[\n\r]/g, '') 73 | //清除 Markdown 表格标记 74 | .replace(/(\|.*?[\n\r]{1,2}){3,}/g, function (match) { 75 | return match.replace(/\|.*?[\n\r]{1,2}/g, function (match) { 76 | if (match.indexOf('---') >= 0) { 77 | return ''; 78 | } else { 79 | return match.replace(/\|/g, ''); 80 | } 81 | }); 82 | }) 83 | //转换一个空白符为一空格 84 | .replace(/[\n\r\t]/g, ' ') 85 | //合并多个空白符为一个空格 86 | .replace(/\s{2,}/g, ' '); 87 | }; 88 | 89 | //给中转处理添加属性 90 | Searcher.prototype._addPorcessing = function (id, key, value) { 91 | //如果不存在此id则创建 92 | if (typeof this._processing[id] == 'undefined') { 93 | this._processing[id] = {}; 94 | this._processing[id][key] = value; 95 | } 96 | //如果存在此id 97 | else { 98 | //如果不存在此属性,直接赋值 99 | if (typeof this._processing[id][key] == 'undefined') { 100 | this._processing[id][key] = value; 101 | } 102 | //如果存在此属性,则相加(仅得分一项) 103 | else { 104 | this._processing[id][key] += value; 105 | } 106 | } 107 | }; 108 | 109 | //匹配搜索词与得分计算 110 | Searcher.prototype.matchWords = function (words) { 111 | var wordsReg = new RegExp(words, 'gi'); 112 | for (var id in this._documents) { 113 | if (this._documents.hasOwnProperty(id)) { 114 | //标题命中 115 | if (this._documents[id].title) { 116 | var titleMatch = this._documents[id].title.match(wordsReg); 117 | if (titleMatch && titleMatch.length > 0) { 118 | var title = this._documents[id].title.replace(wordsReg, function (match) { 119 | return '' + match + ''; 120 | }); 121 | this._addPorcessing(id, 'title', title); 122 | this._addPorcessing(id, 'score', this._data.titleScore); 123 | } 124 | } 125 | //接口地址命中 126 | if (this._documents[id].api) { 127 | var apiMatch = this._documents[id].api.match(wordsReg); 128 | if (titleMatch && titleMatch.length > 0) { 129 | var api = '

接口' + 130 | this._documents[id].api.replace(wordsReg, function (match) { 131 | return '' + match + ''; 132 | }) + '

'; 133 | this._addPorcessing(id, 'api', api); 134 | this._addPorcessing(id, 'score', this._data.apiScore); 135 | } 136 | } 137 | //内容命中 138 | var contentMatch = this._documents[id].content.match(new RegExp('.{0,15}' + words + '.{0,30}', 'gi')); 139 | if (contentMatch) { 140 | var content = '

'; 141 | for (var i = 0, item; item = contentMatch[i]; i++) { 142 | if (i < 2) { 143 | content += item.replace(wordsReg, function (match) { 144 | return '' + match + ''; 145 | }) + '... '; 146 | } 147 | } 148 | content += '

'; 149 | this._addPorcessing(id, 'content', content); 150 | this._addPorcessing(id, 'score', contentMatch.length * this._data.textScore); 151 | } 152 | } 153 | } 154 | this._auxiliary(); 155 | }; 156 | 157 | //辅助得分 158 | Searcher.prototype._auxiliary = function () { 159 | }; 160 | 161 | //排序与属性补齐 162 | Searcher.prototype._sortByScore = function () { 163 | var list = []; 164 | for (var id in this._processing) { 165 | if (this._processing.hasOwnProperty(id)) { 166 | if (typeof this._processing[id].title == 'undefined') { 167 | this._processing[id].title = this._documents[id].title ? this._documents[id].title : ''; 168 | } 169 | if (typeof this._processing[id].api == 'undefined') { 170 | this._processing[id].api = ''; 171 | } 172 | if (typeof this._processing[id].content == 'undefined') { 173 | this._processing[id].content = '

' + this._documents[id].content.substr(0, 45) + '...

'; 174 | } 175 | this._processing[id].path = this._documents[id].uri; 176 | this._processing[id].timestamp = this._documents[id].timestamp; 177 | list.push(this._processing[id]); 178 | } 179 | } 180 | list.sort(function (a, b) { 181 | return a.score > b.score ? -1 : 1; 182 | }); 183 | return list; 184 | }; 185 | 186 | //获取结果 187 | Searcher.prototype.getResult = function () { 188 | return this._sortByScore(); 189 | }; 190 | 191 | return new Searcher(); 192 | 193 | })(); 194 | 195 | self.onmessage = function (event) { 196 | var data = event.data; 197 | if (data.type == 'docs') { 198 | searcher.initDocs(data.docs); 199 | self.postMessage({type: 'ready'}); 200 | } else if (data.type == 'search') { 201 | searcher.matchWords(data.words); 202 | self.postMessage({type: 'result', result: searcher.getResult()}); 203 | } 204 | }; 205 | 206 | self.postMessage({type: 'loaded'}); 207 | 208 | })(self); -------------------------------------------------------------------------------- /docs/amWiki/js/amWiki.storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc amWiki Web端·本地缓存模块 3 | * @author Tevin 4 | */ 5 | 6 | ; 7 | (function (win) { 8 | 9 | 'use strict'; 10 | 11 | var tools = win.tools; 12 | var wikiPath = tools.simString(win.location.pathname.replace('/', '').replace(/\//g, '_')).toUpperCase(); 13 | var LOCAL_STORAGE_NAME = 'AMWikiDataBase@' + wikiPath; //本地数据localStorage键名 14 | 15 | /** 16 | * @class 创建一个本地存储管理对象 17 | */ 18 | var Storage = function () { 19 | this._db = null; //内存中的文库缓存 20 | this.elm = { 21 | $win: $(win), 22 | //更新全部缓存按钮 23 | $searchUpdate: $('#searchUpdate'), 24 | //缓存状态 25 | $cacheState: $('#cacheState'), 26 | //文档总数 27 | $cacheDocTotal: $('#cacheDocTotal'), 28 | //上次全部缓存更新时间 29 | $cacheLasttime: $('#cacheLasttime') 30 | }; 31 | this._bridgeLocalStorage('read'); 32 | this._bindCtrl(); 33 | }; 34 | 35 | //存取本地存储 36 | Storage.prototype._bridgeLocalStorage = function (type) { 37 | if (type == 'read') { 38 | var defaultStr = '{"documents":{},"lastBuild":0}'; 39 | this._db = JSON.parse(win.localStorage[LOCAL_STORAGE_NAME] || defaultStr); 40 | //转换旧名称 41 | if (typeof this._db.libraries != 'undefined') { 42 | this._db.documents = this._db.libraries; 43 | delete this._db.libraries; 44 | } 45 | } else if (type == 'save') { 46 | win.localStorage[LOCAL_STORAGE_NAME] = JSON.stringify(this._db); 47 | } 48 | }; 49 | 50 | //绑定操作 51 | Storage.prototype._bindCtrl = function () { 52 | var that = this; 53 | this.elm.$win.on('beforeunload', function () { 54 | that._bridgeLocalStorage('save'); 55 | }); 56 | }; 57 | 58 | //更新一篇文档,如果相同则不操作(对应内容不用重新渲染) 59 | Storage.prototype.update = function (uri, content) { 60 | var id = tools.simString(uri, 'short'); 61 | if (this._db.documents[id]) { 62 | if (this._db.documents[id].content == content) { 63 | return false; 64 | } else { 65 | this.saveDoc(uri, content, id); 66 | return true; 67 | } 68 | } else { 69 | this.saveDoc(uri, content, id); 70 | return true; 71 | } 72 | }; 73 | 74 | /** 75 | * @desc 保存一篇文档 76 | * @param uri {string} - 文档资源地址 77 | * @param content {string} - 文档内容 78 | * @param [id] {string} - 已经编码的文档地址,可选 79 | */ 80 | Storage.prototype.saveDoc = function (uri, content, id) { 81 | this.saveDocToDB(uri, content, id); 82 | this._bridgeLocalStorage('save'); 83 | this._changeSummary('sateOnly'); 84 | }; 85 | 86 | //将文档存储到内存 87 | Storage.prototype.saveDocToDB = function (uri, content, id) { 88 | if (typeof uri != 'string' && uri == '') { 89 | throw new Error('Error, uri must be a string!'); 90 | } 91 | if (typeof id == 'undefined') { 92 | id = tools.simString(uri, 'short'); 93 | } 94 | this._db.documents[id] = { 95 | id: id, 96 | uri: uri, 97 | content: content || '', 98 | timestamp: Date.now() 99 | }; 100 | this._changeSummary('sateOnly', 'prepare'); 101 | }; 102 | 103 | /** 104 | * @desc 读取一篇文档 105 | * @param uri {string} - 文档资源地址 106 | * @returns {string} - 文档内容 107 | */ 108 | Storage.prototype.read = function (uri) { 109 | var id = tools.simString(uri, 'short'); 110 | var article = ''; 111 | if (this._db.documents[id]) { 112 | article = this._db.documents[id].content; 113 | } 114 | return article; 115 | }; 116 | 117 | /** 118 | * @desc 删除一篇文档 119 | * @param uri {string} - 文档资源地址 120 | */ 121 | Storage.prototype.remove = function (uri) { 122 | var id = tools.simString(uri, 'short'); 123 | delete this._db.documents[id]; 124 | this._bridgeLocalStorage('save'); 125 | this._changeSummary('sateOnly'); 126 | }; 127 | 128 | //增涨文档打开数记录 129 | Storage.prototype.increaseOpenedCount = function (uri) { 130 | var id = tools.simString(uri, 'short'); 131 | //TODO: 待续...打开次数将一定程度影响排行 132 | }; 133 | 134 | /** 135 | * @desc 校对列表,清除失效文档 136 | * @param list {Array} - 由导航树偏平化生成的文档列表 137 | */ 138 | Storage.prototype.checkLibChange = function (list) { 139 | this._indexing = list; 140 | var documents = {}; 141 | var id = ''; 142 | for (var i = 0; i < list.length; i++) { 143 | id = tools.simString(list[i], 'short'); 144 | if (typeof this._db.documents[id] != 'undefined') { 145 | documents[id] = this._db.documents[id]; 146 | } 147 | } 148 | this._db.documents = documents; 149 | this._bridgeLocalStorage('save'); 150 | this._changeSummary(); 151 | }; 152 | 153 | //更新缓存摘要(位于搜素面板) 154 | Storage.prototype._changeSummary = function (stateOnly, prepare) { 155 | var libraryiesLong = 0; 156 | for (var p in this._db.documents) { 157 | if (this._db.documents.hasOwnProperty(p)) { 158 | libraryiesLong++; 159 | } 160 | } 161 | //如果是预先,百分数减1 162 | if (prepare == 'prepare') { 163 | this.elm.$cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100 - 1) + '%'); 164 | } 165 | //非预先则正常 166 | else { 167 | this.elm.$cacheState.text(parseInt(libraryiesLong / this._indexing.length * 100) + '%'); 168 | } 169 | //如果不只是状态 170 | if (stateOnly != 'stateOnly') { 171 | this.elm.$cacheDocTotal.text(this._indexing.length); 172 | if (this._db.lastBuild) { 173 | this.elm.$cacheLasttime.text(win.tools.formatTime(this._db.lastBuild)); 174 | } else { 175 | this.elm.$cacheLasttime.text('0000-00-00 00:00:00'); 176 | } 177 | } 178 | }; 179 | 180 | //清除内存中的库列表 181 | Storage.prototype.clearLibraries = function () { 182 | this._db.documents = {}; 183 | this._changeSummary('sateOnly'); 184 | }; 185 | 186 | //完成本次缓存重建 187 | Storage.prototype.saveRebuild = function () { 188 | this._db.lastBuild = Date.now(); 189 | this._bridgeLocalStorage('save'); 190 | this._changeSummary(); 191 | }; 192 | 193 | //返回导航列表 194 | Storage.prototype.getIndexList = function () { 195 | return this._indexing; 196 | }; 197 | 198 | //获取当前缓存的所有文档 199 | Storage.prototype.getAllDocs = function () { 200 | return this._db.documents; 201 | }; 202 | 203 | //获取缓存最后重建时间 204 | Storage.prototype.getLastBuildTs = function () { 205 | return this._db.lastBuild; 206 | }; 207 | 208 | return win.AWStorage = Storage; 209 | 210 | })(window); -------------------------------------------------------------------------------- /docs/amWiki/js/amWiki.tools.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc amWiki Web端·工具集 3 | * @author Tevin 4 | */ 5 | 6 | ; 7 | (function (win) { 8 | 9 | 'use strict'; 10 | 11 | return win.tools = { 12 | 13 | /** 14 | * @desc 获取url参数 15 | * @param name {string} 16 | * @returns {string|null} - 获取的参数 17 | */ 18 | getURLParameter: function (name) { 19 | var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); 20 | var r = window.location.search.substr(1).match(reg); 21 | if (r != null) { 22 | return r[2]; 23 | } else { 24 | return null; 25 | } 26 | }, 27 | 28 | /** 29 | * @desc 转换字符中每个汉字为两个字符 30 | * @param str {string} - 要编码的字符串 31 | * @param [mod] {string} - 编码模式选择,可选,normal(默认)一个汉字对应两位字符,short一个汉字对应一位字符 32 | * @returns {string} - 编码后的字符串 33 | */ 34 | simString: function (str, mod) { 35 | mod = mod == 'short'; //短字符串 36 | var str2 = ''; 37 | var s = ''; 38 | var encodeURI = win.encodeURI; 39 | for (var i = 0; i < str.length; i++) { 40 | s = str.substr(i, 1); 41 | if (/[\u4e00-\u9fa5]/.test(s)) { 42 | encodeURI(s).split('%').forEach(function (item) { 43 | if (item == '') { 44 | s = []; 45 | } else { 46 | s.push(parseInt('0x' + item)); 47 | } 48 | }); 49 | if (mod) { 50 | str2 += (s[0] + s[1] + s[2]).toString(16).substr(-1, 1); 51 | } else { 52 | str2 += (s[0] + s[1] + s[2]).toString(16).substr(-2, 2); 53 | } 54 | } else { 55 | str2 += s; 56 | } 57 | } 58 | return str2; 59 | }, 60 | 61 | /** 62 | * @desc json格式化 63 | * @param str {string} - 需要格式化的json字符串 64 | * @returns {string} - 格式化后的json字符串 65 | */ 66 | formatJson: function (str) { 67 | var json = decodeURI(str.replace(/%([^0-9A-Z]{0,2})/g, '%25$1')); 68 | var reg = null, 69 | formatted = '', 70 | pad = 0, 71 | PADDING = ' '; 72 | var options = {}; 73 | // remove newline where '{' or '[' follows ':' 74 | options.newlineAfterColonIfBeforeBraceOrBracket = options.newlineAfterColonIfBeforeBraceOrBracket === true; 75 | // use a space after a colon 76 | options.spaceAfterColon = options.spaceAfterColon !== false; 77 | // begin formatting... 78 | if (typeof json !== 'string') { 79 | json = JSON.stringify(json); 80 | } else { 81 | json = JSON.parse(json); 82 | json = JSON.stringify(json); 83 | } 84 | // add newline before and after curly braces 85 | reg = /([\{\}])/g; 86 | json = json.replace(reg, '\r\n$1\r\n'); 87 | // add newline before and after square brackets 88 | reg = /([\[\]])/g; 89 | json = json.replace(reg, '\r\n$1\r\n'); 90 | // add newline after comma 91 | reg = /(\,)/g; 92 | json = json.replace(reg, '$1\r\n'); 93 | // remove multiple newlines 94 | reg = /(\r\n\r\n)/g; 95 | json = json.replace(reg, '\r\n'); 96 | // remove newlines before commas 97 | reg = /\r\n\,/g; 98 | json = json.replace(reg, ','); 99 | // optional formatting... 100 | if (!options.newlineAfterColonIfBeforeBraceOrBracket) { 101 | reg = /\:\r\n\{/g; 102 | json = json.replace(reg, ':{'); 103 | reg = /\:\r\n\[/g; 104 | json = json.replace(reg, ':['); 105 | } 106 | if (options.spaceAfterColon) { 107 | reg = /"\s*\:/g; 108 | json = json.replace(reg, '": '); 109 | } 110 | $.each(json.split('\r\n'), function (index, node) { 111 | var i = 0, 112 | indent = 0, 113 | padding = ''; 114 | if (node.match(/\{$/) || node.match(/\[$/)) { 115 | indent = 1; 116 | } else if (node.match(/\}/) || node.match(/\]/)) { 117 | if (pad !== 0) { 118 | pad -= 1; 119 | } 120 | } else { 121 | indent = 0; 122 | } 123 | for (i = 0; i < pad; i++) { 124 | padding += PADDING; 125 | } 126 | formatted += padding + node + '\r\n'; 127 | pad += indent; 128 | }); 129 | return formatted; 130 | }, 131 | 132 | /** 133 | * @desc 时间戳格式化为日期时间 134 | * @param timestamp {number} - 时间戳 135 | * @returns {string} - 日期时间 136 | */ 137 | formatTime: function (timestamp) { 138 | var time = new Date(timestamp); 139 | var year = time.getFullYear() + ''; 140 | var month = time.getMonth() + 1; 141 | month = month <= 9 ? '0' + month : month; 142 | var date = time.getDate(); 143 | date = date <= 9 ? '0' + date : date; 144 | var hour = time.getHours(); 145 | hour = hour <= 9 ? '0' + hour : hour; 146 | var minute = time.getMinutes(); 147 | minute = minute <= 9 ? '0' + minute : minute; 148 | var second = time.getSeconds(); 149 | second = second <= 9 ? '0' + second : second; 150 | return year + '-' + month + '-' + date + ' ' + hour + ':' + minute + ':' + second; 151 | } 152 | } 153 | 154 | })(window); -------------------------------------------------------------------------------- /docs/assets/000/000-1495808550000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808550000.gif -------------------------------------------------------------------------------- /docs/assets/000/000-1495808586000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808586000.gif -------------------------------------------------------------------------------- /docs/assets/000/000-1495808595000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808595000.gif -------------------------------------------------------------------------------- /docs/assets/000/000-1495808604000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808604000.gif -------------------------------------------------------------------------------- /docs/assets/000/000-1495808615000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808615000.gif -------------------------------------------------------------------------------- /docs/assets/000/000-1495808625000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/000/000-1495808625000.gif -------------------------------------------------------------------------------- /docs/assets/001/02-1495810061000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/001/02-1495810061000.png -------------------------------------------------------------------------------- /docs/assets/001/02-1495810090000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/001/02-1495810090000.png -------------------------------------------------------------------------------- /docs/assets/001/02-1495810116000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/001/02-1495810116000.png -------------------------------------------------------------------------------- /docs/assets/002/001-1495812823000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/001-1495812823000.png -------------------------------------------------------------------------------- /docs/assets/002/002-1495813468000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/002-1495813468000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814105000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814105000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814114000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814114000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814123000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814123000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814132000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814132000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814143000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814143000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814149000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814149000.png -------------------------------------------------------------------------------- /docs/assets/002/003-1495814157000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/003-1495814157000.png -------------------------------------------------------------------------------- /docs/assets/002/004-1495814809000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/004-1495814809000.png -------------------------------------------------------------------------------- /docs/assets/002/004-1495814948000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/004-1495814948000.png -------------------------------------------------------------------------------- /docs/assets/002/004-1495815060000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/004-1495815060000.png -------------------------------------------------------------------------------- /docs/assets/002/006-1495895049000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/006-1495895049000.png -------------------------------------------------------------------------------- /docs/assets/002/006-1495895175000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/006-1495895175000.png -------------------------------------------------------------------------------- /docs/assets/002/006-1495895193000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/006-1495895193000.png -------------------------------------------------------------------------------- /docs/assets/002/006-1495895246000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/006-1495895246000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895465000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895465000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895479000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895479000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895495000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895495000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895510000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895510000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895544000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895544000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495895981000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495895981000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495896031000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495896031000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495896060000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495896060000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495896085000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495896085000.png -------------------------------------------------------------------------------- /docs/assets/002/007-1495896102000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/007-1495896102000.png -------------------------------------------------------------------------------- /docs/assets/002/009-1495897020000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/009-1495897020000.png -------------------------------------------------------------------------------- /docs/assets/002/010-1495897765000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/010-1495897765000.png -------------------------------------------------------------------------------- /docs/assets/002/011-1495898542000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/011-1495898542000.png -------------------------------------------------------------------------------- /docs/assets/002/013-1495899309000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/013-1495899309000.png -------------------------------------------------------------------------------- /docs/assets/002/020-1496319441000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/020-1496319441000.gif -------------------------------------------------------------------------------- /docs/assets/002/023-1496320185000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/023-1496320185000.gif -------------------------------------------------------------------------------- /docs/assets/002/025-1496323861000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/025-1496323861000.png -------------------------------------------------------------------------------- /docs/assets/002/026-1496324134000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/026-1496324134000.gif -------------------------------------------------------------------------------- /docs/assets/002/026-1496324144000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/026-1496324144000.gif -------------------------------------------------------------------------------- /docs/assets/002/029-1496325573000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/029-1496325573000.gif -------------------------------------------------------------------------------- /docs/assets/002/030-1496326622000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/030-1496326622000.png -------------------------------------------------------------------------------- /docs/assets/002/030-1496326635000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/002/030-1496326635000.png -------------------------------------------------------------------------------- /docs/assets/003/02-1497534561000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/003/02-1497534561000.png -------------------------------------------------------------------------------- /docs/assets/003/02-1497534594000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/003/02-1497534594000.png -------------------------------------------------------------------------------- /docs/assets/003/10-1497542012000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/003/10-1497542012000.png -------------------------------------------------------------------------------- /docs/assets/003/11-1497542079000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/003/11-1497542079000.gif -------------------------------------------------------------------------------- /docs/assets/003/12-1497542440000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/003/12-1497542440000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530792000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530792000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530811000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530811000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530852000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530852000.gif -------------------------------------------------------------------------------- /docs/assets/004/01-1497530889000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530889000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530922000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530922000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530969000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530969000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530978000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530978000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530986000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530986000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497530995000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497530995000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531008000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531008000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531033000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531033000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531045000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531045000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531067000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531067000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531086000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531086000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531116000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531116000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531125000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531125000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531134000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531134000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531160000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531160000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531167000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531167000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531173000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531173000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531194000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531194000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531201000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531201000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531209000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531209000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531217000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531217000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531227000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531227000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531259000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531259000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531285000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531285000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531307000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531307000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531330000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531330000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531338000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531338000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531346000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531346000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531362000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531362000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531379000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531379000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531387000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531387000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531472000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531472000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531526000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531526000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531543000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531543000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531552000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531552000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531563000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531563000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531571000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531571000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531590000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531590000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531605000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531605000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531612000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531612000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531619000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531619000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531774000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531774000.png -------------------------------------------------------------------------------- /docs/assets/004/01-1497531795000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/01-1497531795000.png -------------------------------------------------------------------------------- /docs/assets/004/02-1497534785000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/02-1497534785000.gif -------------------------------------------------------------------------------- /docs/assets/004/03-1497540772000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/03-1497540772000.gif -------------------------------------------------------------------------------- /docs/assets/004/04-1497540970000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/04-1497540970000.gif -------------------------------------------------------------------------------- /docs/assets/004/04-1497541302000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/04-1497541302000.gif -------------------------------------------------------------------------------- /docs/assets/004/04-1497541321000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/04-1497541321000.gif -------------------------------------------------------------------------------- /docs/assets/004/04-1497541329000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOUI2/SOUIWiki/cb8c74bc2969e501297bc6bba762ec7fa1537be0/docs/assets/004/04-1497541329000.gif -------------------------------------------------------------------------------- /docs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SOUI", 3 | "ver": "0.1", 4 | "logo": "https://avatars3.githubusercontent.com/u/26452547?v=3&s=200", 5 | "testing": true, 6 | "github-url":"https://github.com/SOUI2/soui", 7 | "colour": "" 8 | } 9 | -------------------------------------------------------------------------------- /docs/library/$navigation.md: -------------------------------------------------------------------------------- 1 | 2 | #### [首页](?file=首页 "返回首页") 3 | 4 | ##### SOUI 概述 5 | - [SOUI特点](?file=001-SOUI 概述/01-SOUI特点 "SOUI特点") 6 | - [SOUI亮点](?file=001-SOUI 概述/02-SOUI亮点 "SOUI亮点") 7 | 8 | ##### 使用教程 9 | - [第一篇:SOUI是什么?](?file=002-使用教程/001-第一篇:SOUI是什么? "第一篇:SOUI是什么?") 10 | - [第二篇:SOUI源码的获取及编译](?file=002-使用教程/002-第二篇:SOUI源码的获取及编译 "第二篇:SOUI源码的获取及编译") 11 | - [第三篇:用SOUI能做什么?](?file=002-使用教程/003-第三篇:用SOUI能做什么? "第三篇:用SOUI能做什么?") 12 | - [第四篇:SOUI资源文件组织](?file=002-使用教程/004-第四篇:SOUI资源文件组织 "第四篇:SOUI资源文件组织") 13 | - [第五篇:在SOUI中使用XML布局属性指引](?file=002-使用教程/005-第五篇:在SOUI中使用XML布局属性指引 "第五篇:在SOUI中使用XML布局属性指引") 14 | - [第六篇:在SOUI中用九宫格拉伸方式显示一个图片资源](?file=002-使用教程/006-第六篇:在SOUI中用九宫格拉伸方式显示一个图片资源 "第六篇:在SOUI中用九宫格拉伸方式显示一个图片资源") 15 | - [第七篇:创建一个SOUI的Hello World](?file=002-使用教程/007-第七篇:创建一个SOUI的Hello World "第七篇:创建一个SOUI的Hello World") 16 | - [第八篇:SOUI中控件事件的响应](?file=002-使用教程/008-第八篇:SOUI中控件事件的响应 "第八篇:SOUI中控件事件的响应") 17 | - [第九篇:在SOUI中使用多语言翻译](?file=002-使用教程/009-第九篇:在SOUI中使用多语言翻译 "第九篇:在SOUI中使用多语言翻译") 18 | - [第十篇:扩展SOUI的控件及绘图对象](?file=002-使用教程/010-第十篇:扩展SOUI的控件及绘图对象 "第十篇:扩展SOUI的控件及绘图对象") 19 | - [第十一篇:SOUI系统资源管理](?file=002-使用教程/011-第十一篇:SOUI系统资源管理 "第十一篇:SOUI系统资源管理") 20 | - [第十二篇:SOUI的utilities模块为什么要用DLL编译?](?file=002-使用教程/012-第十二篇:SOUI的utilities模块为什么要用DLL编译? "第十二篇:SOUI的utilities模块为什么要用DLL编译?") 21 | - [第十三篇:在SOUI中使用有窗口句柄的子窗口](?file=002-使用教程/013-第十三篇:在SOUI中使用有窗口句柄的子窗口 "第十三篇:在SOUI中使用有窗口句柄的子窗口") 22 | - [第十四篇:在SOUI中使用定时器](?file=002-使用教程/014-第十四篇:在SOUI中使用定时器 "第十四篇:在SOUI中使用定时器") 23 | - [第十五篇:在SOUI中消息通讯](?file=002-使用教程/015-第十五篇:在SOUI中消息通讯 "第十五篇:在SOUI中消息通讯") 24 | - [第十六篇:SWindow的布局属性pos2type及offset](?file=002-使用教程/016-第十六篇:SWindow的布局属性pos2type及offset "第十六篇:SWindow的布局属性pos2type及offset") 25 | - [第十七篇:使用窗口的cache属性加速SOUI的渲染](?file=002-使用教程/017-第十七篇:使用窗口的cache属性加速SOUI的渲染 "第十七篇:使用窗口的cache属性加速SOUI的渲染") 26 | - [第十八篇:在SOUI中实现PreTranslateMessage](?file=002-使用教程/018-第十八篇:在SOUI中实现PreTranslateMessage "第十八篇:在SOUI中实现PreTranslateMessage") 27 | - [第十九篇:提高SOUI应用程序渲染性能的三种武器](?file=002-使用教程/019-第十九篇:提高SOUI应用程序渲染性能的三种武器 "第十九篇:提高SOUI应用程序渲染性能的三种武器") 28 | - [第二十篇:在SOUI中使用分层窗口](?file=002-使用教程/020-第二十篇:在SOUI中使用分层窗口 "第二十篇:在SOUI中使用分层窗口") 29 | - [第二十一篇:SOUI中的控件注册机制](?file=002-使用教程/021-第二十一篇:SOUI中的控件注册机制 "第二十一篇:SOUI中的控件注册机制") 30 | - [第二十二篇:在SOUI中使用代码向窗口中插入子窗口](?file=002-使用教程/022-第二十二篇:在SOUI中使用代码向窗口中插入子窗口 "第二十二篇:在SOUI中使用代码向窗口中插入子窗口") 31 | - [第二十三篇:在SOUI中使用LUA脚本开发界面](?file=002-使用教程/023-第二十三篇:在SOUI中使用LUA脚本开发界面 "第二十三篇:在SOUI中使用LUA脚本开发界面") 32 | - [第二十四篇:导出SOUI对象到LUA脚本](?file=002-使用教程/024-第二十四篇:导出SOUI对象到LUA脚本 "第二十四篇:导出SOUI对象到LUA脚本") 33 | - [第二十五篇:在SOUI中做事件分发处理](?file=002-使用教程/025-第二十五篇:在SOUI中做事件分发处理 "第二十五篇:在SOUI中做事件分发处理") 34 | - [第二十六篇:两个SOUI新控件 ---- SListView和SComboView](?file=002-使用教程/026-第二十六篇:两个SOUI新控件 ---- SListView和SComboView "第二十六篇:两个SOUI新控件 ---- SListView和SComboView") 35 | - [第二十七篇:SOUI中控件属性查询方法](?file=002-使用教程/027-第二十七篇:SOUI中控件属性查询方法 "第二十七篇:SOUI中控件属性查询方法") 36 | - [第二十八篇:SOUI中自定义控件开发过程](?file=002-使用教程/028-第二十八篇:SOUI中自定义控件开发过程 "第二十八篇:SOUI中自定义控件开发过程") 37 | - [第二十九篇:使用SOUI的SMCListView控件](?file=002-使用教程/029-第二十九篇:使用SOUI的SMCListView控件 "第二十九篇:使用SOUI的SMCListView控件") 38 | - [第三十篇:SOUI模块结构图及SOUI框架图](?file=002-使用教程/030-第三十篇:SOUI模块结构图及SOUI框架图 "第三十篇:SOUI模块结构图及SOUI框架图") 39 | - [第三十一篇:SOUI布局之相对于特定兄弟窗口](?file=002-使用教程/031-第三十一篇:SOUI布局之相对于特定兄弟窗口 "第三十一篇:SOUI布局之相对于特定兄弟窗口") 40 | - [第三十二篇:在SOUI2.0中像android一样使用资源](?file=002-使用教程/032-第三十二篇:在SOUI2.0中像android一样使用资源 "第三十二篇:在SOUI2.0中像android一样使用资源") 41 | - [第三十三篇:使用uiresImporter生成uires.idx及skin.xml](?file=002-使用教程/033-第三十三篇:使用uiresImporter生成uires.idx及skin.xml "第三十三篇:使用uiresImporter生成uires.idx及skin.xml") 42 | - [第三十四篇:在SOUI中使用异步通知](?file=002-使用教程/034-第三十四篇:在SOUI中使用异步通知 "第三十四篇:在SOUI中使用异步通知") 43 | 44 | ##### 技术分析 45 | - [为GDI函数增加透明度处理](?file=003-技术分析/01-为GDI函数增加透明度处理 "为GDI函数增加透明度处理") 46 | - [实现一种快速查找Richedit中可见区域内OLE对象的方法](?file=003-技术分析/02-实现一种快速查找Richedit中可见区域内OLE对象的方法 "实现一种快速查找Richedit中可见区域内OLE对象的方法") 47 | - [一种快速刷新richedit中内嵌动画的方法的实现](?file=003-技术分析/03-一种快速刷新richedit中内嵌动画的方法的实现 "一种快速刷新richedit中内嵌动画的方法的实现") 48 | - [为什么在soui中加载JPG文件失败](?file=003-技术分析/04-为什么在soui中加载JPG文件失败 "为什么在soui中加载JPG文件失败") 49 | - [一种高效的可变行高列表行定位算法 - 副本](?file=003-技术分析/05-一种高效的可变行高列表行定位算法 - 副本 "一种高效的可变行高列表行定位算法 - 副本") 50 | - [在SOUI中非半透明窗口如何实现圆角窗口?](?file=003-技术分析/06-在SOUI中非半透明窗口如何实现圆角窗口? "在SOUI中非半透明窗口如何实现圆角窗口?") 51 | - [SOUI与WTL](?file=003-技术分析/07-SOUI与WTL "SOUI与WTL") 52 | - [不注册COM在Richedit中使OLE支持复制粘贴](?file=003-技术分析/08-不注册COM在Richedit中使OLE支持复制粘贴 "不注册COM在Richedit中使OLE支持复制粘贴") 53 | - [在SOUI中使用线性布局](?file=003-技术分析/09-在SOUI中使用线性布局 "在SOUI中使用线性布局") 54 | - [在SOUI中使用窗口自適應大小](?file=003-技术分析/10-在SOUI中使用窗口自適應大小 "在SOUI中使用窗口自適應大小") 55 | - [在SOUI中使用动态多语言切换](?file=003-技术分析/11-在SOUI中使用动态多语言切换 "在SOUI中使用动态多语言切换") 56 | - [在SOUI中支持高分屏显示](?file=003-技术分析/12-在SOUI中支持高分屏显示 "在SOUI中支持高分屏显示") 57 | 58 | ##### 案例演示 59 | - [基于SOUI开发的应用展示](?file=004-案例演示/01-基于SOUI开发的应用展示 "基于SOUI开发的应用展示") 60 | - [使用SOUI开发的界面集锦](?file=004-案例演示/02-使用SOUI开发的界面集锦 "使用SOUI开发的界面集锦") 61 | - [SOUI中做的一个磁力吸附效果](?file=004-案例演示/03-SOUI中做的一个磁力吸附效果 "SOUI中做的一个磁力吸附效果") 62 | - [SOUI Editor使用教程](?file=004-案例演示/04-SOUI Editor使用教程 "SOUI Editor使用教程") 63 | -------------------------------------------------------------------------------- /docs/library/001-SOUI 概述/01-SOUI特点.md: -------------------------------------------------------------------------------- 1 | 原文链接:[《UI神器-SOUI》](http://www.cnblogs.com/setoutsoft/p/4996870.html) 2 | 3 | - **使用层**:高速,稳定,美观,可配置 4 | 5 | - **代码层**:精心设计,模块低耦合,插件化设计,对象可靠的命令周期管理,类似WTL的编码方式,现代化的事件处理模型及优异的扩展能力。 6 | 7 | - **代码量**:核心模块代码量4W+,编译后DLL Release版本在900K左右。得益于精心组织的代码框架,虽然代码量较Duilib这样的UI库有比较大的提高(核心框架更完善,控件更多,注释量更大),但是阅读代码还是很轻松的(大量实际用户的亲身体验)。 8 | 9 | - **高速**:主要体现在**3**个方面: 10 | 11 | >[1] 框架设计扁平化,层次简单(和QT相比):从宿主窗口收到消息到控件响应消息只有一个中间层。 12 | 13 | >[2] 简单有效的刷新策略:通过对剪裁区及刷新时机的有较控制, 能有效的提高刷新效率。 14 | 15 | >[3] 高效的渲染引擎:通过 将渲染引擎接口化,成功的将skia渲染引擎引入到SOUI中,Skia是Google的Chrome的渲染引擎,Chrome比IE渲染速度快,Skia功不可没。 16 | 17 | - **稳定性**:SOUI脱胎于Bkwin,再经过本人的不断精心重构,已经在多个大量用户的产品中应用,包括最近开发的瑞雪医生客户端,多玩魔盒2.0, Dota2游戏盒子及多玩多个游戏盒子中使用, 及百度云管家的大部分界面。 18 | 百度云管家据说最初使用的是腾讯QQ界面库的早期版本(无从考证),然而QQ界面库大量使用COM技术,扩展非常麻烦,使用很是不便,在后续的UI需求中开始大量使用SOUI的前身DuiEngine。 19 | 20 | - **美观**:SOUI原生支持Alpha通道,能够实现各种半透明效果,包括主窗体半透明,DUI窗口半透明,DUI窗口模仿LayeredWindow(分层窗口)效果等,轻松实现各种异形效果。 21 | 22 | - **可配置**:SOUI中所有UI资源都采用XML描述,调整UI效果一般只需要修改XML资源即可完成。 23 | 24 | >说到代码层的设计很难用语言描述,只有亲自阅读代码方能理解。为大部分需要在外部(APP层)经常引用的UI相关对象提供引用计数设计能够有效减少C++开发中常见的野指针问题,这一点还是很好体会,同时系统中也重点解决了如消息分发的分层设计,窗口对象的消息重入等影响UI使用体验的关键性问题。 -------------------------------------------------------------------------------- /docs/library/001-SOUI 概述/02-SOUI亮点.md: -------------------------------------------------------------------------------- 1 | 原文链接:[《UI神器-SOUI》](http://www.cnblogs.com/setoutsoft/p/4996870.html) 2 | 3 | >宽泛的说SOUI多好大家并没直观的感觉,下面从一些具体的点来介绍SOUI。 4 | 5 | #### 界面布局 6 | 也许初学者对于SOUI的布局还不太适应,特别是对于那些习惯了Duilib的布局方式的朋友。事实上SOUI的布局应该是最接近程序思维的布局方式。前段时间开发Android,仅仅是它的5大Layout就能让人崩溃,而且不同的layout对应的布局属性还不一样。 7 | SOUI的布局非常简单,只有两个布局属性:pos + offset,具体参考博客:http://www.cnblogs.com/setoutsoft/p/3925952.html 8 | 通常使用一个pos属性就解决布局问题了,pos在XML中使用"x1,y1,x2,y2"这样的4个坐标定义一个控件在父窗口中的相对位置,而offset则定义通过pos计算出来的位置后在X,Y两个方向需要叠加的偏移,偏移值需要乘上窗口大小。 9 | 例如下面这个需求: 10 | 11 | ![](assets/001/02-1495810061000.png) 12 | 13 | 只知道窗口需要靠右下角,不知道窗口大小的情况,在SOUI中只需要使用属性`pos=“-20,-30” offset="-1,-1"`即可。 14 | 15 | #### 渲染流程 16 | 一个UI中的界面元素最后会通过各级子窗口形成一个树状结构。一般的渲染流程自然是从根节点一层一层的直到渲染完成所有叶结点。这个过程很简单,可能很多UI库也就做到这个层次(例如DuiLib)。但是对于一个高性能的UI库仅做到这个层次是不够的,举例来说:一个画笔程序需要在OnMouseMove里面绘制新拾取的线条,本能的做法是获取窗口画布,绘制完成后再提交画布(类似Windows API: `GetDC and ReleaseDC`),而不是每一次绘制只能请求宿主刷新(请求宿主立即刷新依赖于系统对UpdateWindow这个API的响应速度)。 17 | 因此一个成熟的UI引擎有必要实现GetDC及ReleaseDC这样的接口。和基于HWND的窗口获取HDC不同,在一套DirectUI系统中实现GetDC及ReleaseDC要更加复杂:最关键的问题在于获取前绘制窗口的背景,以及提交后绘制窗口的前景,要实现窗口背景前景的分开绘制又需要系统提供绘制在指定Z-Order范围内的窗口的能力,当然前提是系统中有Z-Order这样的概念。 18 | 就算实现了窗口的背景与前景的分别绘制,对于一个高性能的UI引擎可能还是不够的。因为有些时候一个窗口中的内容是不需要和背景混合的,窗口刷新的时候绘制背景是没有意义的(如视频播放窗口),就是需要另一种技术:窗口的跨层渲染(不知道这样命名是不是合适)。当一个视频窗口需要刷新的时候,它的刷新流程和基本的刷新流程是不一样的,渲染时它会跳过它的所有父窗口直接到这个窗口层来,从而大大加速渲染过程。 19 | 20 | #### 分层窗口 21 | Windows的分层窗口是Windows 2000提供的一项重要更新。苹果系统的UI很漂亮,有了分层窗口,Windows系统上开发的应用也可以同样漂亮。 22 | 这里说的分层窗口有两个层次:一个是DirectUI的宿主窗口中使用分层窗口技术;另一层是在DirectUI的DUI窗口系统内部实现分层窗口技术。 23 | 使用分层窗口技术听起来比较简单,不就是设计一个`WS_EX_LAYEREDWINDOW`属性再使用`UpdateLayeredWindow(EX)`更新窗口吗?!如果SOUI只达到这个层次,那和codeproject上随便找一个demo也没有什么区别。 24 | 首先要搞清楚,SOUI是一套DirectUI系统,而不是Demo,因此它不能停留在加载一个32位PNG图片并显示出来这样的层次上。它必须要能够让用户能够调用各种绘制图形,图像,文字的API来组合出一个最终需要呈现的32位位图。这一点要求看起来简单,在Windows系统上实现起来并不简单,因为Windows上最基本的绘制API(GDI)都是不支持alpha通道的。有一个简单的选择:GDIPlus。然而GDIPlus有一个毛病就是速度太慢,这对于一个通用的UI引擎来说,全部依赖GDIPlus基本上就宣判了这个引擎的死刑。在SOUI中采用渲染引擎抽象的方法实现了两种渲染引擎:Skia + GDI。前面不是说GDI不支持Alpha通过不能用吗?没错,直接用GDI函数是不行的,我们需要适当的改造(具体方法参见代码)。 25 | 解决了绘制方法,要更新到窗口中显示也还是有技巧的。有人可能知道,使用UpdateLayeredWindow这个API更新的窗口将收不到WM_PAINT消息。由于在半透明窗口中不能直接支持有窗口句柄的子窗口的显示(如IE控件),SOUI还必须为那些需要容纳窗口句柄子窗口的情况提供支持,即通过配置同时支持半透明窗口与不透明窗口。但是我不愿意为两种不同的最终位图呈现模型提供两套不同的机制。解决的办法很简单,通过为半透明类型的窗口设计一个辅助窗口,使用它来接收WM_PAINT消息,收到该消息时调用UpdateLayeredWindow更新窗口。注: 这个技术是学习另一套UI库MetalBone实现的。 26 | 讲完了使用宿主窗口分层窗口,下面讲讲DUI窗口的分层窗口技术的实现。 27 | 使用分层窗口技术能够使UI效果更漂亮,关键技术就在这个层。层是什么?层是一组窗口的绘制容器,它将该层下所有子窗口的绘制内容绘制到一个独立的缓冲区上, 最后再一起绘制到分层窗口的上一层绘制缓冲区中。如下图: 28 | 29 | ![](assets/001/02-1495810090000.png) 30 | 31 | A、B、B1、B2、C为DUI系统中5个DUI窗口。其中,B、B1、B2是同一个渲染层。也就是说设计需要它们先绘制好后再和A,C做融合。 类似的需求对于一个漂亮的UI来说可能会很常见。如果在UI引擎中没有层的概念是不可能实现的。如果不需要实现前面提到的背景和前景分别渲染的情况,实现会层窗口其实也不难,只需要在渲染到B窗口时创建一个缓冲区,把从B开始的内容渲染到这个缓冲区,完成后再回到正常渲染流程,就像没有B1、B2一样。但是SOUI是支持背景前景分别渲染的,实现这个过程的代码逻辑就可能很复杂了(可以自己想象一下)。 32 | 33 | #### 非客户区 34 | HWND的非客户区用来绘制滚动条及边框及标题栏,菜单栏。客户区是用户绘制的常规区域,在设计上将窗口的显示区域划分为客户区和非客户区,有利于用户在重写客户区的绘制代码时不被非客户区干扰,也有利于代码的复用。 35 | 在DuiLib中,一个控件如Richedit需要显示滚动条,它需要给这个控件组合两个滚动条控件。这种方式虽然看上去没有什么大的问题,如果由于窗口中内容的变化需要动态显示隐藏滚动条时可能会很麻烦,至少它会引起窗口布局系统的重排,因为滚动条显示和隐藏时控件的客户区大小是变化的。 36 | 而在SOUI系统中,滚动条和HWND一样,用户根本不需要关心,因为内部已经自动处理好了滚动条,也不会引起布局系统的重排。 37 | 38 | #### 资源加载 39 | 一般来说SOUI中引用的所有资源都在XML中描述。刚入门的朋友通常反映SOUI中使用资源的方式不如DuiLib直接,很难入门。但是一旦真正理解了SOUI的这种资源组织方式一定会更喜欢SOUI。 40 | SOUI提供3种资源加载方式:文件,PE资源,ZIP包。 41 | 首先SOUI的资源包必须提供一个文件索引表,对于使用PE资源的资源包,索引表就是资源的类型及ID,而对于直接使用文件或者ZIP包的资源,索引表则是一个XML文件。在索引表中,定义每一个资源的type及name两个KEY,SOUI界面布局中只能使用type和name两个key来引用资源。 42 | 用户只需要准备一套文件资源,如果需要将资源编译到PE文件中,系统提供一个工具直接从文件资源的索引XML转换成rc编译器可以识别的rc文件;而如果用户需要使用ZIP资源包,则只需要使用一个ZIP工具如rar, 7z将资源文件夹打包即可(推荐使用7z打包资源,SOUI内自带的zlib 1.2.5能够识别7z打包的带密码的zip包,但不能识别rar打包的带密码的zip包。 43 | 44 | #### 窗口动画改进 45 | 一般情况下我们推荐使用窗口定时器来创建动画。使用窗口定时器创建动画的好处是定时器和UI是同一个线程,而SOUI不支持多线程同步更新UI(事实上一般的DirectUI库都不推荐在工作线程中操作UI,如Android)。那么问题来了,如果为每一个DUI窗口创建很多定时器,那么系统的消息队列中将充满定时器消息,严重时可能大大降低UI性能。 46 | 解决方案:在主窗口中创建一个10ms间隔的定时器,需要处理动画的窗口向系统注册使用该定时器,动画记录下一次动画需要等待的时间,使用该统一的定时器计数。 47 | 我们看一下面DEMO中显示大量动画表情时SOUI的效率: 48 | 49 | ![](assets/001/02-1495810116000.png) 50 | 51 | 这一CPU占用率甚至比QQ中同样情况下还低。 52 | 53 | #### 容器分层 54 | 什么叫容器分层?在DirectUI中所有的DUI窗口都必须生存在一个容器中。DUI窗口的绘制请求等最终需要由这个容器来实现。在容器不分层的情况下,所有DUI窗口在容器中的物理坐标都是从(0,0)开始。这样有什么问题呢?如果要在列表控件的列表项中使用DUI控件就会变得非常麻烦,因为在窗口滚动时你可能不得不同时更新所有这些控件的坐标。 55 | 有了窗口层的概念就不一样了,每珍上列表项是一个新的容器,无论列表项显示在哪,列表项中的控件(容器中的控件)的坐标都不需要调整。因为有了容器分层,在SOUI中实现包含子控件的列表变得非常简单(参考下节:高性能列表控件)。 56 | 57 | #### 高性能列表控件 58 | Windows系统中提供的列表控件非常简单,只能满足简单的数据显示需求。注意,是显示。然而现在的UI需求中经常出现那种即时修改列表控件内容的情况,你将不得不花大量的时间对列表控件进行自绘,而效果只能说勉强。 59 | 通过研究Android系统中提供的列表控件的代码,借鉴Android中ListView的思想,SOUI实现了一套高性能的列表控件SListView及SMcListView。 60 | SListView及SMcListView都是基于虚表技术,同时只创建当前正在显示的及部分备用的列表项容器,将资源占用缩小到最少。同时ListView在滚动时能够高效刷新,实现了海量数据的高性能显示及更新。 61 | 实现这个高性能列表控件的关键有两点: 62 | 首先是SOUI中实现的容器层的概念,使得列表位位置变化时,容器内部的控件不需要调整坐标。 63 | 其次就是容器数据的充分重用。 64 | 65 | ![](assets/000/000-1495808625000.gif) 66 | 67 | 注:上面列表中只测试了7W行数据,实际上listview中显示的数据量多少完全不影响UI性能,亲测700W行数据和7W行效果一样。 68 | 69 | #### 无窗口Richedit 70 | Edit控件是UI中最常用的控件之一。在允许存在子窗口句柄的情况下,系统Edit控件已经能够很好的满足我们的需求。然而在不允许子窗口句柄的情况下,实现一个Edit控件会非常麻烦。 71 | 当然,程序可以选择自己去重新实现一套edit,Edit也许还可行,一般情况下要实现一个Richedit基本不可行。 好在实现Richedit的模块riched20.dll中把UI和逻辑分离开来,即可以用它直接创建有窗口的Richedit,也可以用它来创建提供无窗口Richedit的ITextServices接口。然而即使是这样,程序员需要为ITextServices实现一个ITextHost接口。尽管MSDN上有相关的文档及示例,但是根据它们提供的这些资料实现的效果很不理想。必竟只是Demo,不是完整的代码,它不能演示开发中可能遇到的每一个细节。然而恰好是这些细节是影响UI用户体验的关键。 72 | 所以我们需要另辟蹊径来解决这个问题。我解决这些细节,关键在于理解它们的逻辑。SOUI的办法是找到riched20.dll的源代码。好在网络上流传着一份从WinCE源代码中分离出来的Riched20.dll的源代码,虽然用它编译出来的Richedit有很多BUG,但利用它可以让我们更好的理解各种细节。大家可以测试SOUI中的Edit,效果应该是各种类似库中最好的一个之一。 73 | 74 | #### XML+LUA 75 | 部分模块在SOUI中采用了接口化设计,如前面提到的渲染引擎,以及后面要说的多语言翻译,以及这里要说的脚本模块。 76 | 脚本语言方便灵活,更新简单,LUA脚本还有高效的特点。和WEB的`HTML+JS`类似,SOUI实现了`XML+LUA`的UI开发解决方案。`XML实现UI布局,LUA实现逻辑控制`。 77 | 78 | >**实现方法**: 79 | 在XML中使用`