Sciter.js 指南 - 自定义GUI程序头部-微信聊天窗

这个文档会教你怎么样自定义一个自己样式的窗口,不要长得和操作系统原生的样。这时有定制样式,功能,比如关闭,最大化,最小化。还有点击什么位置能让程序拖动。

为什么需要自定义窗口头部?

默认情况下,操作系统会为应用程序窗口提供标准的标题栏。但在许多情况下,您可能希望:

  • 品牌化: 让窗口外观与您的应用程序设计保持一致。
  • 添加自定义控件: 在标题栏中加入搜索框、工具栏按钮或其他 UI 元素。
  • 独特的视觉风格: 创建一个完全自定义的、非标准的窗口外观。

Sciter.js 提供了强大的机制,让您可以使用 HTML 和 CSS 完全控制窗口的外观和行为。

核心概念:window-frame="extended"

要开始自定义窗口头部,最关键的一步是在您主 HTML 文件的根 <html> 元素上设置 window-frame="extended" 属性。这告诉 Sciter 您将自己绘制窗口的非客户区(包括标题栏和边框)。

html 复制代码
<html window-frame="extended" window-width="800px" window-height="600px">
  <head>
    <title>我的自定义窗口</title>
    <style src="window.css" />
    <script type="module" src="main.js" />
  </head>
  <body>
    <!-- 窗口内容 -->
  </body>
</html>

使用 HTML 构建头部结构

设置 window-frame="extended" 后,您需要使用 HTML 元素来定义窗口头部的结构。通常,这会放在 <body> 标签之前,或者作为 <body> 的第一个子元素(取决于您的布局)。一个常见的结构是使用 <header> 元素。

关键在于使用特定的 role 属性来告诉 Sciter 哪些元素扮演标准窗口控件的角色:

  • role="window-caption": 必需。指定一个元素作为窗口标题栏。用户可以通过拖动此元素来移动窗口。

  • role="window-icon": (可选)指定一个元素作为窗口图标。在 Windows 上,单击此图标通常会显示窗口菜单。

  • role="window-minimize": (可选)使元素表现为最小化按钮。

  • role="window-maximize": (可选)使元素表现为最大化/还原按钮。

  • role="window-close": (可选)使元素表现为关闭按钮。

    重要提示 : 当用户点击带有 role="window-minimize", role="window-maximize", 或 role="window-close" 属性的元素时,Sciter 会自动处理相应的窗口操作(最小化、最大化/还原、关闭),您通常不需要为此编写额外的 JavaScript 事件处理代码。

示例 HTML 结构:

html 复制代码
<header>
  <img role="window-icon" src="images/my-icon.svg" />
  <window-caption role="window-caption">我的应用程序标题</window-caption>
  <window-buttons>
    <window-button role="window-minimize"></window-button>
    <window-button role="window-maximize"></window-button>
    <window-button role="window-close"></window-button>
  </window-buttons>
</header>

在这个例子中:

  • 我们使用 <header> 作为容器。
  • 一个 <img> 标签用作窗口图标。
  • 一个 <window-caption> (或任何其他元素,如 <span>, <div>)用作可拖动的标题区域。
  • 一个 <window-buttons> 容器(通常是 <div> 或自定义元素)包含标准的窗口控制按钮。
  • <window-button> (通常是 <button> 或自定义元素)扮演最小化、最大化和关闭的角色。 注意这些也可以直接写进 css 的类当中,也是相当的方便。

使用 CSS 设计头部样式

一旦有了 HTML 结构,您就可以使用 CSS 来完全控制头部的外观。

关键 CSS 属性和技术:

  • 布局 (flow: horizontal) : 通常,头部元素(图标、标题、按钮)是水平排列的。flow: horizontal; 是实现这一点的常用方法。
  • 高度 (height: window-caption-height) : Sciter 提供了一个 CSS 变量 window-caption-height,它代表了操作系统标准标题栏的高度。使用它可以让您的自定义头部具有合适的高度。
  • 宽度 (width: *) : 对于像标题这样需要填充可用空间的元素,width: *; (星号宽度) 非常有用。
  • 背景和颜色 : 使用 background-color, color 等标准 CSS 属性来设置背景和文本颜色。
  • 按钮样式 :
    • 大小 : width: window-button-width; 可以用来获取标准按钮宽度。
    • 图标 : 使用 foreground-image (对于 Sciter 的矢量图标路径) 或 background-image 来设置按钮图标。
    • 描边/填充 (stroke, fill) : 对于矢量图标,使用 strokefill 来控制颜色。
    • 悬停效果 (:hover): 定义鼠标悬停在按钮上时的样式变化(例如,改变背景色)。
  • 平台特定样式 (@media platform) : 您可以使用 @media 查询来为不同操作系统(如 Windows, Linux, OSX)应用不同的样式。

使用 JavaScript 自定义行为 (可选)

虽然 Sciter 会自动处理带有 role="window-..." 属性的元素的默认行为(最小化、最大化、关闭),但在某些高级场景下,您可能希望通过 JavaScript 完全控制这些按钮的点击事件。例如,在关闭窗口前显示一个确认对话框。

为此,您可以:

  1. 不使用 role 属性 : 如果您想完全接管,可以不在按钮上设置 role="window-close" 等属性。
  2. 添加事件监听器 : 使用 JavaScript 为您的按钮元素(例如,通过 ID 或 class 选择)添加 click 事件监听器。
  3. 调用窗口 API: 在事件处理函数中,调用相应的 Sciter 窗口 API 来执行所需的操作。

示例:自定义关闭按钮

假设您的关闭按钮 HTML 是:

html 复制代码
<button id="my-close-button">关闭</button>

您的 sciter.js 中的 JS 代码可以这样写:

javascript 复制代码
on["click", "button#my-close-button"], (evt) {
    // 阻止可能的默认行为(如果设置了 role)
    // event.stopPropagation(); // 通常不需要,除非 role 存在且引起冲突

    // 显示确认对话框
    const confirmed = Window.this.modal(<alert caption="确认">确定要关闭窗口吗?</alert>);

    if (confirmed) {
        Window.this.close(); // 调用关闭窗口 API
    }
});

类似地,您可以使用以下 API 来自定义其他按钮:

  • 最小化 : Window.this.state = Window.WINDOW_MINIMIZED;
  • 最大化/还原 : 通过检查当前状态 Window.this.state (例如 Window.WINDOW_MAXIMIZED) 来决定是最大化 (Window.this.state = Window.WINDOW_MAXIMIZED;) 还是还原 (Window.this.state = Window.WINDOW_RESTORED;)。

注意: 如果您选择使用 JavaScript 处理事件,请确保您的逻辑覆盖了所有必要的窗口状态管理。

完整示例

这是使用 sciter.js 写的微信一样的头部。

下面是一个将前面讨论的概念整合在一起的微信风格的自定义 GUI 的软件头部的完整示例。

html 复制代码
<!DOCTYPE html>
<html window-frame="extended" window-width="800px" window-height="600px"  window-corners="not-round" >

<head>
    <meta charset="UTF-8">
    <title>微信</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft YaHei", sans-serif;
        }

        html {
            border-radius: 0px;
        }

        body {
            background-color: #f5f5f5;
        }

        .container {
            flow: horizontal;
            height: *;
        }

        .sidebar {
            width: 60px;
            background-color: #2c2c2c;
            height: 100%;
            flow: vertical;
            content-horizontal-align: center;
            padding-top: 20px;
        }

        .avatar {
            width: 50px;
            height: 50px;
            border-radius: 4px;
            overflow: hidden;
            background-color: #555;
            text-align: center;
            line-height: 50px;
            font-size: 35px;
        }

        .main {
            width: 1*;
            flow: vertical;
        }

        .header {
            height: 56px;
            background-color: #f6f6f6;
            border-bottom: 1px solid #e0e0e0;
            flow: horizontal;
            padding: 0 10px;
            position: relative;
        }

        .search-area {
            flow: horizontal;
            vertical-align: middle;
            width: 340px;
        }

        .search-container {
            flow: horizontal;
            align-items: center;
            background-color: #e6e6e6;
            border-radius: 4px;
            padding: 4px 10px;
            width: 300px;
            height: 24px;
        }

        .search-icon {
            color: #999;
            margin-right: 8px;
            font-size: 16px;
        }

        .search-input {
            background: none;
            border: none;
            outline: none;
            width: 1*;
            color: #999;
            font-size: 12px;
        }

        .add-button {
            width: 24px;
            height: 24px;
            border-radius: 4px;
            background-color: #e6e6e6;
            border: none;
            margin-left: 10px;
            font-size: 16px;
            cursor: pointer;
            text-align: center;
            line-height: 24px;
            color: #666;
        }

        .divider {
            width: 0.5px;
            height: 100%;
            background-color: #e0e0e0;
            margin: 0 20px;
        }

        .user-info {
            color: #333;
            font-weight: bold;
            font-size: 16px;
            width: 1*;
            padding-left: 20px;
            line-height: 56px;
            min-width: 200px;
            role: window-caption;
            /* 按这块就可以拖动 */
        }

        .controls-container {
            flow: vertical;
        }

        .window-controls {
            margin-top: 8px;
            flow: horizontal;
        }

        .control-button {
            background: none;
            border: none;
            cursor: pointer;
            size: 16px;
            margin-left: 15px;
            margin-right: 5px;
            stroke: #666;
        }

        .control-button:hover {
            stroke: #333;
        }

        .control-button:nth-child(1) {
            role: window-minimize;
            foreground-image: url(icon:window-minimize);
        }

        .control-button:nth-child(2) {
            role: window-maximize;
            foreground-image: url(icon:window-maximize);
        }

        .control-button:nth-child(3) {
            role: window-close;
            foreground-image: url(icon:window-close);
        }

        .control-button:nth-child(3):hover {
            stroke: #ff0000;
        }

        .more-button-container {
            margin-top: 4px;
            margin-right: 10px;
            text-align: right;
        }

        .more-button {
            background: none;
            border: none;
            cursor: pointer;
            size: 16px;
            stroke: #666;
            foreground-image: url(icon:opts-h);
            foreground-size: 18px;
            margin-left: *;
        }

        .more-button:hover {
            stroke: #333;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="sidebar">
            <div class="avatar">
                👨
            </div>
        </div>
        <div class="main">
            <div class="header">
                <div class="search-area">
                    <div class="search-container">
                        <span class="search-icon">🔍</span>
                        <input type="text" class="search-input" placeholder="搜索">
                    </div>
                    <button class="add-button">+</button>
                </div>
                <div class="divider"></div>
                <div class="user-info">凯哥1970</div>
                <div class="controls-container">
                    <div class="window-controls">
                        <button class="control-button"></button>
                        <button class="control-button"></button>
                        <button class="control-button"></button>
                    </div>
                    <div class="more-button-container">
                        <button class="more-button"></button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

最佳实践

  1. 保持简洁: 除非绝对必要,否则不要在标题栏中添加过多的交互元素,以免让用户混淆。
  2. 明确可拖动区域 : 确保 role="window-caption" 的元素足够大且易于点击和拖动。
  3. 提供标准控件 : 除非您的 UI 设计有充分理由省略它们,否则应始终提供最小化、最大化和关闭按钮,并赋予它们正确的 role
  4. 使用 CSS 变量 : 利用 Sciter 提供的变量(如 window-caption-height, window-button-width, window-accent-color)来适应系统设置。
  5. 分离关注点: 将 HTML 结构、CSS 样式和 JavaScript 行为分别放在不同的文件中,以提高可维护性。
  6. 测试: 在目标平台上测试您的自定义头部,确保外观和行为符合预期。

总结

通过在 <html> 标签上设置 window-frame="extended",并结合使用带有特定 role 属性的 HTML 元素和 CSS 样式,您可以完全控制 Sciter.js 应用程序窗口的外观。这为您提供了创建美观、品牌化和功能丰富的用户界面的能力。

查阅 Sciter SDK 中的示例(特别是 samples.sciter/window/chrome-types/demos/usciter/ 目录下的示例)以获取更多灵感和实现细节。

相关推荐
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax