diff --git a/vlib/gg/gg.c.v b/vlib/gg/gg.c.v index 0808da32bb..0502b022e1 100644 --- a/vlib/gg/gg.c.v +++ b/vlib/gg/gg.c.v @@ -34,6 +34,63 @@ pub mut: framebuffer_height int } +pub struct Config { +pub: + width int + height int + use_ortho bool // unused, still here just for backwards compatibility + retina bool + resizable bool + user_data voidptr + font_size int + create_window bool + // window_user_ptr voidptr + window_title string + borderless_window bool + always_on_top bool + bg_color gx.Color + init_fn FNCb = voidptr(0) + frame_fn FNCb = voidptr(0) + native_frame_fn FNCb = voidptr(0) + cleanup_fn FNCb = voidptr(0) + fail_fn FNFail = voidptr(0) + // + event_fn FNEvent = voidptr(0) + quit_fn FNEvent = voidptr(0) + // + keydown_fn FNKeyDown = voidptr(0) + keyup_fn FNKeyUp = voidptr(0) + char_fn FNChar = voidptr(0) + // + move_fn FNMove = voidptr(0) + click_fn FNClick = voidptr(0) + unclick_fn FNUnClick = voidptr(0) + leave_fn FNEvent = voidptr(0) + enter_fn FNEvent = voidptr(0) + resized_fn FNEvent = voidptr(0) + scroll_fn FNEvent = voidptr(0) + // wait_events bool // set this to true for UIs, to save power + fullscreen bool + scale f32 = 1.0 + sample_count int + swap_interval int = 1 // 1 = 60fps, 2 = 30fps etc. The preferred swap interval (ignored on some platforms) + // ved needs this + // init_text bool + font_path string + custom_bold_font_path string + ui_mode bool // refreshes only on events to save CPU usage + // font bytes for embedding + font_bytes_normal []byte + font_bytes_bold []byte + font_bytes_mono []byte + font_bytes_italic []byte + native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows + // drag&drop + enable_dragndrop bool // enable file dropping (drag'n'drop), default is false + max_dropped_files int = 1 // max number of dropped files to process (default: 1) + max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048) +} + [heap] pub struct Context { mut: diff --git a/vlib/gg/gg.js.v b/vlib/gg/gg.js.v index bf845f5ed6..e0690a1ca5 100644 --- a/vlib/gg/gg.js.v +++ b/vlib/gg/gg.js.v @@ -1,5 +1,6 @@ module gg +import gx import js.dom pub enum DOMEventType { @@ -55,37 +56,6 @@ pub mut: framebuffer_height int } -pub struct Context { -mut: - render_text bool = true - image_cache []Image - needs_refresh bool = true - ticks int -pub mut: - scale f32 = 1.0 - width int - height int - window JS.Window - config Config - user_data voidptr - ui_mode bool - frame u64 - mbtn_mask byte - mouse_buttons MouseButtons - mouse_pos_x int - mouse_pos_y int - mouse_dx int - mouse_dy int - scroll_x int - scroll_y int - // - key_modifiers Modifier // the current key modifiers - key_repeat bool // whether the pressed key was an autorepeated one - pressed_keys [key_code_max]bool // an array representing all currently pressed keys - pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys, - // *before* the current event was different -} - pub enum DOMMouseButton { invalid = -1 left = 0 @@ -226,3 +196,152 @@ pub enum DOMKeyCode { right_super = 347 menu = 348 } + +pub struct Config { +pub: + width int + height int + use_ortho bool // unused, still here just for backwards compatibility + retina bool + resizable bool + user_data voidptr + font_size int + create_window bool + // window_user_ptr voidptr + window_title string + borderless_window bool + always_on_top bool + bg_color gx.Color + init_fn FNCb = voidptr(0) + frame_fn FNCb = voidptr(0) + native_frame_fn FNCb = voidptr(0) + cleanup_fn FNCb = voidptr(0) + fail_fn FNFail = voidptr(0) + // + event_fn FNEvent = voidptr(0) + quit_fn FNEvent = voidptr(0) + // + keydown_fn FNKeyDown = voidptr(0) + keyup_fn FNKeyUp = voidptr(0) + char_fn FNChar = voidptr(0) + // + move_fn FNMove = voidptr(0) + click_fn FNClick = voidptr(0) + unclick_fn FNUnClick = voidptr(0) + leave_fn FNEvent = voidptr(0) + enter_fn FNEvent = voidptr(0) + resized_fn FNEvent = voidptr(0) + scroll_fn FNEvent = voidptr(0) + // wait_events bool // set this to true for UIs, to save power + fullscreen bool + scale f32 = 1.0 + sample_count int + swap_interval int = 1 // 1 = 60fps, 2 = 30fps etc. The preferred swap interval (ignored on some platforms) + // ved needs this + // init_text bool + font_path string + custom_bold_font_path string + ui_mode bool // refreshes only on events to save CPU usage + // font bytes for embedding + font_bytes_normal []byte + font_bytes_bold []byte + font_bytes_mono []byte + font_bytes_italic []byte + native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows + // drag&drop + enable_dragndrop bool // enable file dropping (drag'n'drop), default is false + max_dropped_files int = 1 // max number of dropped files to process (default: 1) + max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048) + context JS.CanvasRenderingContext2D +} + +pub struct Context { +mut: + render_text bool = true + image_cache []Image + needs_refresh bool = true + ticks int +pub mut: + scale f32 = 1.0 + width int + height int + window JS.Window [noinit] + config Config + user_data voidptr + ui_mode bool + frame u64 + mbtn_mask byte + mouse_buttons MouseButtons + mouse_pos_x int + mouse_pos_y int + mouse_dx int + mouse_dy int + scroll_x int + scroll_y int + // + key_modifiers Modifier // the current key modifiers + key_repeat bool // whether the pressed key was an autorepeated one + pressed_keys [key_code_max]bool // an array representing all currently pressed keys + pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys, + canvas JS.CanvasRenderingContext2D [noinit] + // *before* the current event was different +} + +pub fn new_context(cfg Config) &Context { + mut g := &Context{} + g.user_data = cfg.user_data + g.width = cfg.width + g.height = cfg.height + g.ui_mode = cfg.ui_mode + g.config = cfg + if isnil(cfg.user_data) { + g.user_data = g + } + g.window = dom.window() + g.canvas = cfg.context + + return g +} + +pub fn (mut ctx Context) run() { + gg_animation_frame_fn(mut ctx) +} + +pub fn (mut ctx Context) begin() { + // ctx.canvas.beginPath() +} + +pub fn (mut ctx Context) end() { + // ctx.canvas.closePath() +} + +pub fn (mut ctx Context) draw_line(x1 f32, y1 f32, x2 f32, y2 f32, c gx.Color) { + ctx.canvas.beginPath() + ctx.canvas.strokeStyle = c.to_css_string().str + ctx.canvas.moveTo(x1, y1) + ctx.canvas.lineTo(x2, y2) + ctx.canvas.stroke() + ctx.canvas.closePath() +} + +pub fn (mut ctx Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) { + ctx.canvas.beginPath() + ctx.canvas.fillStyle = c.to_css_string().str + ctx.canvas.fillRect(x, y, w, h) + ctx.canvas.closePath() +} + +fn gg_animation_frame_fn(mut g Context) { + g.frame++ + g.canvas.clearRect(0, 0, g.config.width, g.config.height) + // todo(playXE): handle events + if !isnil(g.config.frame_fn) { + f := g.config.frame_fn + f(g.user_data) + g.needs_refresh = false + } + + g.window.requestAnimationFrame(fn [mut g] (time JS.Number) { + gg_animation_frame_fn(mut g) + }) +} diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index 9283ec1c47..3b1f154f2c 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -23,63 +23,6 @@ pub type FNUnClick = fn (x f32, y f32, button MouseButton, data voidptr) pub type FNChar = fn (c u32, data voidptr) -pub struct Config { -pub: - width int - height int - use_ortho bool // unused, still here just for backwards compatibility - retina bool - resizable bool - user_data voidptr - font_size int - create_window bool - // window_user_ptr voidptr - window_title string - borderless_window bool - always_on_top bool - bg_color gx.Color - init_fn FNCb = voidptr(0) - frame_fn FNCb = voidptr(0) - native_frame_fn FNCb = voidptr(0) - cleanup_fn FNCb = voidptr(0) - fail_fn FNFail = voidptr(0) - // - event_fn FNEvent = voidptr(0) - quit_fn FNEvent = voidptr(0) - // - keydown_fn FNKeyDown = voidptr(0) - keyup_fn FNKeyUp = voidptr(0) - char_fn FNChar = voidptr(0) - // - move_fn FNMove = voidptr(0) - click_fn FNClick = voidptr(0) - unclick_fn FNUnClick = voidptr(0) - leave_fn FNEvent = voidptr(0) - enter_fn FNEvent = voidptr(0) - resized_fn FNEvent = voidptr(0) - scroll_fn FNEvent = voidptr(0) - // wait_events bool // set this to true for UIs, to save power - fullscreen bool - scale f32 = 1.0 - sample_count int - swap_interval int = 1 // 1 = 60fps, 2 = 30fps etc. The preferred swap interval (ignored on some platforms) - // ved needs this - // init_text bool - font_path string - custom_bold_font_path string - ui_mode bool // refreshes only on events to save CPU usage - // font bytes for embedding - font_bytes_normal []byte - font_bytes_bold []byte - font_bytes_mono []byte - font_bytes_italic []byte - native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows - // drag&drop - enable_dragndrop bool // enable file dropping (drag'n'drop), default is false - max_dropped_files int = 1 // max number of dropped files to process (default: 1) - max_dropped_file_path_length int = 2048 // max length in bytes of a dropped UTF-8 file path (default: 2048) -} - pub struct PenConfig { color gx.Color line_type PenLineType = .solid diff --git a/vlib/gg/text_rendering.c.v b/vlib/gg/text_rendering.c.v index 3e6116ea3c..1a2a3b7e41 100644 --- a/vlib/gg/text_rendering.c.v +++ b/vlib/gg/text_rendering.c.v @@ -228,3 +228,113 @@ pub fn (ctx &Context) text_size(s string) (int, int) { ctx.ft.fons.text_bounds(0, 0, s, &buf[0]) return int((buf[2] - buf[0]) / ctx.scale), int((buf[3] - buf[1]) / ctx.scale) } + +pub fn system_font_path() string { + env_font := os.getenv('VUI_FONT') + if env_font != '' && os.exists(env_font) { + return env_font + } + $if windows { + debug_font_println('Using font "C:\\Windows\\Fonts\\arial.ttf"') + return 'C:\\Windows\\Fonts\\arial.ttf' + } + $if macos { + fonts := ['/System/Library/Fonts/SFNS.ttf', '/System/Library/Fonts/SFNSText.ttf', + '/Library/Fonts/Arial.ttf'] + for font in fonts { + if os.is_file(font) { + debug_font_println('Using font "$font"') + return font + } + } + } + $if android { + xml_files := ['/system/etc/system_fonts.xml', '/system/etc/fonts.xml', + '/etc/system_fonts.xml', '/etc/fonts.xml', '/data/fonts/fonts.xml', + '/etc/fallback_fonts.xml'] + font_locations := ['/system/fonts', '/data/fonts'] + for xml_file in xml_files { + if os.is_file(xml_file) && os.is_readable(xml_file) { + xml := os.read_file(xml_file) or { continue } + lines := xml.split('\n') + mut candidate_font := '' + for line in lines { + if line.contains('').all_before('<').trim(' \n\t\r') + if candidate_font.contains('.ttf') { + for location in font_locations { + candidate_path := os.join_path(location, candidate_font) + if os.is_file(candidate_path) && os.is_readable(candidate_path) { + debug_font_println('Using font "$candidate_path"') + return candidate_path + } + } + } + } + } + } + } + } + mut fm := os.execute("fc-match --format='%{file}\n' -s") + if fm.exit_code == 0 { + lines := fm.output.split('\n') + for l in lines { + if !l.contains('.ttc') { + debug_font_println('Using font "$l"') + return l + } + } + } else { + panic('fc-match failed to fetch system font') + } + panic('failed to init the font') +} + +fn get_font_path_variant(font_path string, variant FontVariant) string { + // TODO: find some way to make this shorter and more eye-pleasant + // NotoSans, LiberationSans, DejaVuSans, Arial and SFNS should work + mut file := os.file_name(font_path) + mut fpath := font_path.replace(file, '') + file = file.replace('.ttf', '') + + match variant { + .normal {} + .bold { + if fpath.ends_with('-Regular') { + file = file.replace('-Regular', '-Bold') + } else if file.starts_with('DejaVuSans') { + file += '-Bold' + } else if file.to_lower().starts_with('arial') { + file += 'bd' + } else { + file += '-bold' + } + $if macos { + if os.exists('SFNS-bold') { + file = 'SFNS-bold' + } + } + } + .italic { + if file.ends_with('-Regular') { + file = file.replace('-Regular', '-Italic') + } else if file.starts_with('DejaVuSans') { + file += '-Oblique' + } else if file.to_lower().starts_with('arial') { + file += 'i' + } else { + file += 'Italic' + } + } + .mono { + if !file.ends_with('Mono-Regular') && file.ends_with('-Regular') { + file = file.replace('-Regular', 'Mono-Regular') + } else if file.to_lower().starts_with('arial') { + // Arial has no mono variant + } else { + file += 'Mono' + } + } + } + return fpath + file + '.ttf' +} diff --git a/vlib/gg/text_rendering.v b/vlib/gg/text_rendering.v index 77a1a0e201..80ff0dfff8 100644 --- a/vlib/gg/text_rendering.v +++ b/vlib/gg/text_rendering.v @@ -2,7 +2,6 @@ // Use of this source code is governed by an MIT license that can be found in the LICENSE file. module gg -import os import gx enum FontVariant { @@ -30,116 +29,6 @@ struct StringToRender { cfg gx.TextCfg } -pub fn system_font_path() string { - env_font := os.getenv('VUI_FONT') - if env_font != '' && os.exists(env_font) { - return env_font - } - $if windows { - debug_font_println('Using font "C:\\Windows\\Fonts\\arial.ttf"') - return 'C:\\Windows\\Fonts\\arial.ttf' - } - $if macos { - fonts := ['/System/Library/Fonts/SFNS.ttf', '/System/Library/Fonts/SFNSText.ttf', - '/Library/Fonts/Arial.ttf'] - for font in fonts { - if os.is_file(font) { - debug_font_println('Using font "$font"') - return font - } - } - } - $if android { - xml_files := ['/system/etc/system_fonts.xml', '/system/etc/fonts.xml', - '/etc/system_fonts.xml', '/etc/fonts.xml', '/data/fonts/fonts.xml', - '/etc/fallback_fonts.xml'] - font_locations := ['/system/fonts', '/data/fonts'] - for xml_file in xml_files { - if os.is_file(xml_file) && os.is_readable(xml_file) { - xml := os.read_file(xml_file) or { continue } - lines := xml.split('\n') - mut candidate_font := '' - for line in lines { - if line.contains('').all_before('<').trim(' \n\t\r') - if candidate_font.contains('.ttf') { - for location in font_locations { - candidate_path := os.join_path(location, candidate_font) - if os.is_file(candidate_path) && os.is_readable(candidate_path) { - debug_font_println('Using font "$candidate_path"') - return candidate_path - } - } - } - } - } - } - } - } - mut fm := os.execute("fc-match --format='%{file}\n' -s") - if fm.exit_code == 0 { - lines := fm.output.split('\n') - for l in lines { - if !l.contains('.ttc') { - debug_font_println('Using font "$l"') - return l - } - } - } else { - panic('fc-match failed to fetch system font') - } - panic('failed to init the font') -} - -fn get_font_path_variant(font_path string, variant FontVariant) string { - // TODO: find some way to make this shorter and more eye-pleasant - // NotoSans, LiberationSans, DejaVuSans, Arial and SFNS should work - mut file := os.file_name(font_path) - mut fpath := font_path.replace(file, '') - file = file.replace('.ttf', '') - - match variant { - .normal {} - .bold { - if fpath.ends_with('-Regular') { - file = file.replace('-Regular', '-Bold') - } else if file.starts_with('DejaVuSans') { - file += '-Bold' - } else if file.to_lower().starts_with('arial') { - file += 'bd' - } else { - file += '-bold' - } - $if macos { - if os.exists('SFNS-bold') { - file = 'SFNS-bold' - } - } - } - .italic { - if file.ends_with('-Regular') { - file = file.replace('-Regular', '-Italic') - } else if file.starts_with('DejaVuSans') { - file += '-Oblique' - } else if file.to_lower().starts_with('arial') { - file += 'i' - } else { - file += 'Italic' - } - } - .mono { - if !file.ends_with('Mono-Regular') && file.ends_with('-Regular') { - file = file.replace('-Regular', 'Mono-Regular') - } else if file.to_lower().starts_with('arial') { - // Arial has no mono variant - } else { - file += 'Mono' - } - } - } - return fpath + file + '.ttf' -} - [if debug_font ?] fn debug_font_println(s string) { println(s) diff --git a/vlib/gx/color.v b/vlib/gx/color.v index aaec4016e5..bdb551ec91 100644 --- a/vlib/gx/color.v +++ b/vlib/gx/color.v @@ -232,3 +232,7 @@ const ( pub fn color_from_string(s string) Color { return gx.string_colors[s] } + +pub fn (c Color) to_css_string() string { + return 'rgb($c.r,$c.g,$c.b)' +} diff --git a/vlib/gx/text.js.v b/vlib/gx/text.js.v new file mode 100644 index 0000000000..c477a72eee --- /dev/null +++ b/vlib/gx/text.js.v @@ -0,0 +1,14 @@ +module gx + +pub enum HorizontalAlign { + left + center + right +} + +pub enum VerticalAlign { + top + middle + bottom + baseline +} diff --git a/vlib/js/dom/dom.js.v b/vlib/js/dom/dom.js.v index 2ae042c092..a2f2c9e154 100644 --- a/vlib/js/dom/dom.js.v +++ b/vlib/js/dom/dom.js.v @@ -451,7 +451,7 @@ pub interface JS.CanvasRenderingContext2D { getLineDash() JS.Array setLineDash(segments JS.Array) clearRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) - fillRect(x JS.Number, y JS.Number, w JS.null, h JS.Number) + fillRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) strokeRect(x JS.Number, y JS.Number, w JS.Number, h JS.Number) getTransformt() JS.DOMMatrix resetTransform() diff --git a/vlib/os/environment.js.v b/vlib/os/environment.js.v index 3324844636..67ae5bd345 100644 --- a/vlib/os/environment.js.v +++ b/vlib/os/environment.js.v @@ -3,6 +3,7 @@ module os $if js_node { #global.$ENV = $process.env } $else { + #const global = $global; #global.$ENV = {} } diff --git a/vlib/os/file.js.v b/vlib/os/file.js.v index 61aa7b940d..273727b51a 100644 --- a/vlib/os/file.js.v +++ b/vlib/os/file.js.v @@ -7,8 +7,9 @@ pub mut: is_opened bool } -#const $buffer = require('buffer'); - +$if !js_browser { + #const $buffer = require('buffer'); +} // todo(playX): __as_cast is broken here /* pub struct ErrFileNotOpened { diff --git a/vlib/os/os.js.v b/vlib/os/os.js.v index e9ae812f01..fff2f90a7a 100644 --- a/vlib/os/os.js.v +++ b/vlib/os/os.js.v @@ -1,8 +1,10 @@ module os -#const $fs = require('fs'); -#const $path = require('path'); -#const tty = require('tty') +$if js_node { + #const $fs = require('fs'); + #const $path = require('path'); + #const tty = require('tty') +} pub const ( path_delimiter = '/' diff --git a/vlib/os/process.js.v b/vlib/os/process.js.v index b5ae53efdd..cae2c6646a 100644 --- a/vlib/os/process.js.v +++ b/vlib/os/process.js.v @@ -1,6 +1,8 @@ module os -#const $child_process = require('child_process') +$if js_node { + #const $child_process = require('child_process') +} // new_process - create a new process descriptor // NB: new does NOT start the new process. diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3e1e40c31c..1ec850bc85 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -827,7 +827,14 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } // Do not allow empty uninitialized interfaces sym := c.table.sym(field.typ) - if sym.kind == .interface_ { + mut has_noinit := false + for attr in field.attrs { + if attr.name == 'noinit' { + has_noinit = true + break + } + } + if sym.kind == .interface_ && (!has_noinit && sym.language != .js) { // TODO: should be an error instead, but first `ui` needs updating. c.note('interface field `${type_sym.name}.$field.name` must be initialized', node.pos) diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index c756855f4f..0a90fe910d 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -461,10 +461,11 @@ pub fn (mut g JsGen) init() { g.definitions.writeln('const \$process = {') g.definitions.writeln(' arch: "js",') if g.pref.backend == .js_freestanding { - g.definitions.writeln(' platform: "freestanding"') + g.definitions.writeln(' platform: "freestanding",') } else { - g.definitions.writeln(' platform: "browser"') + g.definitions.writeln(' platform: "browser",') } + g.definitions.writeln(' cwd: function() { return "" }') g.definitions.writeln('}') g.definitions.writeln('const \$os = {') @@ -3263,12 +3264,8 @@ fn (mut g JsGen) gen_struct_init(it ast.StructInit) { g.writeln('return tmp') g.dec_indent() g.writeln('})()') - } else { - if type_sym.kind == .struct_ && type_sym.language == .js { - g.writeln('{') - } else { - g.writeln('new ${g.js_name(name)}({') - } + } else if type_sym.kind == .struct_ && type_sym.language == .js { + g.writeln('{') g.inc_indent() for i, field in it.fields { if field.name.len != 0 { @@ -3281,11 +3278,26 @@ fn (mut g JsGen) gen_struct_init(it ast.StructInit) { g.writeln('') } g.dec_indent() - if type_sym.kind == .struct_ && type_sym.language == .js { - g.writeln('}') - } else { - g.writeln('})') + + g.writeln('}') + } else { + g.writeln('(function() {') + g.inc_indent() + tmp := g.new_tmp_var() + g.writeln('let $tmp = new ${g.js_name(name)}({});') + + for field in it.fields { + if field.name.len != 0 { + g.write('${tmp}.$field.name = ') + g.expr(field.expr) + } + g.write(';') + + g.writeln('') } + g.writeln('return $tmp;') + g.dec_indent() + g.writeln('})()') } } @@ -3385,6 +3397,18 @@ fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) { return } + if (from_type_sym.name == 'Any' && from_type_sym.language == .js) + || from_type_sym.name == 'JS.Any' { + if it.typ.is_ptr() { + g.write('new \$ref(') + } + g.expr(it.expr) + if it.typ.is_ptr() { + g.write(')') + } + return + } + // Skip cast if type is the same as the parrent caster tsym := to_type_sym if tsym.kind == .sum_type {