Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ project(net

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

option(BUILD_UI "Enable Sciter UI" OFF)
option(BUILD_TESTS "Enable unit tests" ON)
option(BUILD_SAMPLES "Build provided samples" OFF)
option(ENABLE_COVERAGE "Enable coverage reporting" OFF)
Expand All @@ -24,6 +25,10 @@ add_subdirectory(subscriber)
add_subdirectory(client)
add_subdirectory(server)

if (WIN32 AND BUILD_UI)
add_subdirectory(ui-sciter)
endif ()

if (BUILD_TESTS)
include(CTest)
enable_testing()
Expand Down
30 changes: 16 additions & 14 deletions cmakegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ class IBuilder(ABC):
'''
Abstract class to generate a project for some IDEs.
'''
def __init__(self, generator, build_type, build_tests, build_samples, enable_coverage):
def __init__(self, generator, build_type, build_ui, build_tests, build_samples, enable_coverage):
self.build_dir = 'build'
self.generator = generator
self.build_type = build_type
self.build_ui = build_ui
self.build_tests = build_tests
self.build_samples = build_samples
self.enable_coverage = enable_coverage
Expand All @@ -33,15 +34,15 @@ def generate_project(self):
pass

def get_cmake_generate_command(self):
return 'cmake -G "{}" .. -DCMAKE_BUILD_TYPE={} -DBUILD_TESTS={} -DBUILD_SAMPLES={} -DENABLE_COVERAGE={}'.format(
self.generator, self.build_type, self.build_tests, self.build_samples, self.enable_coverage)
return 'cmake -G "{}" .. -DCMAKE_BUILD_TYPE={} -DBUILD_UI={} -DBUILD_TESTS={} -DBUILD_SAMPLES={} -DENABLE_COVERAGE={}'.format(
self.generator, self.build_type, self.build_ui, self.build_tests, self.build_samples, self.enable_coverage)

class XcodeBuilder(IBuilder):
'''
Class to generate a project for Xcode IDE.
'''
def __init__(self, build_type, build_tests, build_samples, enable_coverage):
super().__init__('Xcode', build_type, build_tests, build_samples, enable_coverage)
def __init__(self, build_type, build_ui, build_tests, build_samples, enable_coverage):
super().__init__('Xcode', build_type, build_ui, build_tests, build_samples, enable_coverage)

def create_build_dir(self):
run_cmd('mkdir -p {}'.format(self.build_dir))
Expand All @@ -54,8 +55,8 @@ class VisualStudioBuilder(IBuilder):
'''
Class to generate a project for Visual Studio IDE.
'''
def __init__(self, build_type, build_tests, build_samples, enable_coverage):
super().__init__('Visual Studio 17 2022', build_type, build_tests, build_samples, enable_coverage)
def __init__(self, build_type, build_ui, build_tests, build_samples, enable_coverage):
super().__init__('Visual Studio 17 2022', build_type, build_ui, build_tests, build_samples, enable_coverage)
self.platform = 'x64'

def create_build_dir(self):
Expand All @@ -69,8 +70,8 @@ class UnixBuilder(IBuilder):
'''
Class to generate a project for QtCreator IDE.
'''
def __init__(self, build_type, build_tests, build_samples, enable_coverage):
super().__init__('CodeBlocks - Unix Makefiles', build_type, build_tests, build_samples, enable_coverage)
def __init__(self, build_type, build_ui, build_tests, build_samples, enable_coverage):
super().__init__('CodeBlocks - Unix Makefiles', build_type, build_ui, build_tests, build_samples, enable_coverage)

def create_build_dir(self):
run_cmd('mkdir -p {}'.format(self.build_dir))
Expand All @@ -79,27 +80,28 @@ def create_build_dir(self):
def generate_project(self):
run_cmd(super().get_cmake_generate_command())

def create_builder(build_type, build_tests, build_samples, enable_coverage):
def create_builder(build_type, build_ui, build_tests, build_samples, enable_coverage):
'''
Factory method that makes a project builder for the specific platform.
'''
platform_name = platform.system()
if platform_name == 'Darwin':
return XcodeBuilder(build_type, build_tests, build_samples, enable_coverage)
return XcodeBuilder(build_type, build_ui, build_tests, build_samples, enable_coverage)
elif platform_name == 'Windows':
return VisualStudioBuilder(build_type, build_tests, build_samples, enable_coverage)
return VisualStudioBuilder(build_type, build_ui, build_tests, build_samples, enable_coverage)
elif platform_name == 'Linux':
return UnixBuilder(build_type, build_tests, build_samples, enable_coverage)
return UnixBuilder(build_type, build_ui, build_tests, build_samples, enable_coverage)

def main():
parser = argparse.ArgumentParser()
parser.add_argument('--build-type', '-b', dest='build_type', default='Debug')
parser.add_argument('--build-ui', '-u', action='store_true')
parser.add_argument('--build-tests', '-t', action='store_true')
parser.add_argument('--build-samples', '-s', action='store_true')
parser.add_argument('--enable-coverage', '-c', action='store_true')
args = parser.parse_args()

builder = create_builder(args.build_type, args.build_tests, args.build_samples, args.enable_coverage)
builder = create_builder(args.build_type, args.build_ui, args.build_tests, args.build_samples, args.enable_coverage)
builder.create_build_dir()
builder.generate_project()

Expand Down
1 change: 1 addition & 0 deletions ui-sciter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sciter-*.*
52 changes: 52 additions & 0 deletions ui-sciter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.16.0)

project(sciter CXX)

set(SCITER_ROOT $ENV{SCITERSDK})
set(SCITER_BIN $ENV{SCITERSDK}/bin)

set(SCITER_MAIN_SOURCE_NAME
$<$<CXX_COMPILER_ID:MSVC>:sciter-win-main.cpp>
$<$<CXX_COMPILER_ID:Clang>:sciter-osx-main.mm>
)

set(SCITER_MAIN_SOURCE_PATH ${SCITER_ROOT}/include/${SCITER_MAIN_SOURCE_NAME})

set(SCITER_DYNAMIC_LIBRARY_PATH
$<$<CXX_COMPILER_ID:MSVC>:${SCITER_BIN}/windows/x64/sciter.dll>
$<$<CXX_COMPILER_ID:Clang>:${SCITER_BIN}/macosx/libsciter.dylib>
)

add_executable(${PROJECT_NAME} WIN32
${SCITER_MAIN_SOURCE_PATH}
main.cpp
)

target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)

target_include_directories(${PROJECT_NAME}
PRIVATE
${SCITER_ROOT}/include
)

target_link_libraries(${PROJECT_NAME}
PRIVATE
fmt::fmt
)

add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${SCITER_BIN}/windows/packfolder.exe
${CMAKE_CURRENT_SOURCE_DIR}/ui
${CMAKE_CURRENT_SOURCE_DIR}/sciter-resources.cpp
-v "resources"
)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${SCITER_DYNAMIC_LIBRARY_PATH}
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}
)

if (WIN32)
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DPI_AWARE "PerMonitor")
endif ()
15 changes: 15 additions & 0 deletions ui-sciter/issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Gradient direction

# Sciter does not support `display: flex`. It has [flow:vertical | horizontal | etc.](https://terrainformatica.com/w3/flex-layout/flex-vs-flexbox.htm) instead.

## Browser

```html
display: flex; flex-direction: row; justify-content: start; align-items: center;
```

## Sciter

```html
flow: horizontal; vertical-align: middle;
```
39 changes: 39 additions & 0 deletions ui-sciter/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "sciter-x-window.hpp"
#include "sciter-x.h"

class MainWindow : public sciter::window
{
public:
MainWindow()
: window(SW_MAIN | SW_ENABLE_DEBUG | SW_CONTROLS)
{
}

// passport - lists native functions and properties exposed to script under
// 'MainWindow' interface name:
SOM_PASSPORT_BEGIN(MainWindow)
SOM_FUNCS(SOM_FUNC(getMessage), SOM_FUNC(quite))
SOM_PASSPORT_END

// function exposed to script:
sciter::string getMessage() { return WSTR("Hello, C++ World!"); }
void quite() { close(); }
};

#include "sciter-resources.cpp" // resources packaged into binary blob.

int uimain(std::function<int()> run)
{
// Bind resources[] (defined in "sciter-resources.cpp") with the archive.
sciter::archive::instance().open(aux::elements_of(resources));

sciter::om::hasset<MainWindow> pwin = new MainWindow();

// this:://app URL is dedicated to the sciter::archive content
// associated with the app.
pwin->load(WSTR("this://app/main.htm"));

pwin->expand();

return run();
}
103 changes: 103 additions & 0 deletions ui-sciter/ui/chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Chart as ChartJS, registerables } from "chartjs/chart.esm.js";

ChartJS.register(...registerables);

// local redifinition: we do not need canvas.style.height / width redefines;
function retinaScale(chart, forceRatio, forceStyle) {
const pixelRatio = forceRatio || 1;
const deviceHeight = Math.floor(chart.height * pixelRatio);
const deviceWidth = Math.floor(chart.width * pixelRatio);
chart.height = deviceHeight / pixelRatio;
chart.width = deviceWidth / pixelRatio;
const canvas = chart.canvas;
/*AF: if (canvas.style && (forceStyle || (!canvas.style.height && !canvas.style.width))) {
canvas.style.height = `${chart.height}px`;
canvas.style.width = `${chart.width}px`;
}*/
if (
chart.currentDevicePixelRatio !== pixelRatio ||
canvas.height !== deviceHeight ||
canvas.width !== deviceWidth
) {
chart.currentDevicePixelRatio = pixelRatio;
canvas.height = deviceHeight;
canvas.width = deviceWidth;
chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
return true;
}
return false;
}

function doResize(width, height) {
// this == chart
const options = this.options;
const canvas = this.canvas;
const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
const mode = this.width ? "resize" : "attach";
this.width = newSize.width;
this.height = newSize.height;
this._aspectRatio = this.aspectRatio;
if (!retinaScale(this, newRatio, false)) {
return;
}
this.notifyPlugins("resize", { size: newSize });
if (this.attached) {
if (this._doResize(mode)) this.render();
}
}

export class Chart extends Element {
chartjs = null;
config = {};

this(props, kids) {
if (props.config) {
this.config = this.#setupConfig(props.config);
if (this.chartjs) this.chartjs.update("none"); // update the chart but without animations
}
}

render() {
return <canvas.chart />;
}

componentDidMount() {
var ctx = this.getContext("2d");

var gradient1 = ctx.createLinearGradient(0, 100, 0, 400);
gradient1.addColorStop(0, "rgba(45, 163, 173, 1)");
gradient1.addColorStop(1, "rgba(45, 163, 173, 0)");
this.config.data.datasets[1].backgroundColor = gradient1;

var gradient2 = ctx.createLinearGradient(0, 200, 0, 400);
gradient2.addColorStop(0, "rgba(195, 197, 211, 1)");
gradient2.addColorStop(1, "rgba(195, 197, 211, 0)");
this.config.data.datasets[0].backgroundColor = gradient2;

this.chartjs = new ChartJS(this, this.config);
this.chartjs._resize = doResize;
this.post(this.onsizechange);
}

componentWillUnmount() {
this.chartjs.destroy();
this.chartjs = null;
}

onsizechange() {
if (this.chartjs) {
let [w, h] = this.state.box("dimension");
this.chartjs.resize(w, h);
}
}

#setupConfig(c) {
c = c || {};
c.options = c.options || {};
c.options.responsive = false;
c.options.maintainAspectRatio = false;
return c;
}
}
Loading