@@ -2,10 +2,13 @@ const std = @import("std");
const dvui = @import ( " dvui " ) ;
const dvui_ext = @import ( " dvui_ext.zig " ) ;
const Canvas = @import ( " ../Canvas.zig " ) ;
const Document = @import ( " ../models/Document.zig " ) ;
const Property = @import ( " ../models/Property.zig " ) . Property ;
const PropertyData = @import ( " ../models/Property.zig " ) . Data ;
const Rect_i = @import ( " ../models/basic_models.zig " ) . Rect_i ;
const Tool = @import ( " ../toolbar/Tool.zig " ) ;
pub fn canvasView ( canvas : * Canvas , content_rect_scale : dvui . RectScale ) void {
pub fn canvasView ( canvas : * Canvas , selected_object : ? * Document . Object , content_rect_scale : dvui . RectScale ) void {
var textured = dvui_ext . texturedBox ( content_rect_scale , dvui . Rect . all ( 20 ) ) ;
{
var overlay = dvui . overlay ( @src ( ) , . { . expand = . both } ) ;
@@ -45,6 +48,27 @@ pub fn canvasView(canvas: *Canvas, content_rect_scale: dvui.RectScale) void {
canvas . toolbar_rect_scale = toolbar_box . data ( ) . contentRectScale ( ) ;
toolbar_box . deinit ( ) ;
// Панель свойств поверх scroll (правый верхний угол)
if ( selected_object ) | obj | {
var properties_box = dvui . box (
@src ( ) ,
. { . dir = . horizontal } ,
. {
. expand = . none ,
. background = false ,
. gravity_x = 1.0 ,
. gravity_y = 0.0 ,
. margin = dvui . Rect { . w = 8 , . y = 8 } ,
} ,
) ;
{
drawPropertiesPanel ( canvas , obj ) ;
}
// Сохраняем rect панели свойств для следующего кадра — в handleCanvasMouse исключаем из него клики
canvas . properties_rect_scale = properties_box . data ( ) . contentRectScale ( ) ;
properties_box . deinit ( ) ;
}
dvui . label ( @src ( ) , " Canvas " , . { } , . { . gravity_x = 0.5 , . gravity_y = 0.0 } ) ;
}
overlay . deinit ( ) ;
@@ -181,6 +205,12 @@ fn handleCanvasMouse(canvas: *Canvas, scroll: *dvui.ScrollAreaWidget) void {
const r = trs . r ;
if ( pt . x > = 0 and pt . x * trs . s < r . w and pt . y > = 0 and pt . y * trs . s < r . h ) continue ;
}
// Н е обрабатывать клик, если он попал в область панели свойств (rect с предыдущего кадра).
if ( canvas . properties_rect_scale ) | prs | {
const pt = prs . pointFromPhysical ( mouse . p ) ;
const r = prs . r ;
if ( pt . x > = 0 and pt . x * prs . s < r . w and pt . y > = 0 and pt . y * prs . s < r . h ) continue ;
}
const viewport_pt = scroll_data . contentRectScale ( ) . pointFromPhysical ( mouse . p ) ;
const content_pt = dvui . Point {
@@ -242,3 +272,218 @@ fn drawToolbar(canvas: *Canvas) void {
}
bar . deinit ( ) ;
}
fn drawPropertiesPanel ( canvas : * Canvas , selected_object : * Document . Object ) void {
var panel = dvui . box (
@src ( ) ,
. { . dir = . vertical } ,
. {
. gravity_x = 1.0 ,
. gravity_y = 0.0 ,
. margin = dvui . Rect { . x = 8 , . y = 8 } ,
. padding = dvui . Rect . all ( 8 ) ,
. corner_radius = dvui . Rect . all ( 8 ) ,
. background = true ,
. color_fill = dvui . Color . black . opacity ( 0.2 ) ,
. min_size_content = . { . w = 220 } ,
} ,
) ;
{
dvui . label ( @src ( ) , " Properties " , . { } , . { } ) ;
for ( selected_object . properties . items , 0 . . ) | * prop , i | {
drawPropertyEditor ( canvas , selected_object , prop , i ) ;
}
}
panel . deinit ( ) ;
}
fn drawPropertyEditor ( canvas : * Canvas , obj : * Document . Object , prop : * const Property , row_index : usize ) void {
const row_id : usize = row_index * 16 ;
var row = dvui . box (
@src ( ) ,
. { . dir = . vertical } ,
. {
. id_extra = row_id ,
. expand = . horizontal ,
. padding = dvui . Rect { . y = 2 } ,
} ,
) ;
{
const tag = std . meta . activeTag ( prop . data ) ;
dvui . labelNoFmt ( @src ( ) , propertyLabel ( tag ) , . { } , . { } ) ;
switch ( prop . data ) {
. position = > | pos | {
var next = pos ;
const doc = canvas . document ;
const min_x = - doc . size . w ;
const max_x = doc . size . w ;
const min_y = - doc . size . h ;
const max_y = doc . size . h ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " x: {d:0.2} " , . { . value = & next . x , . min = min_x , . max = max_x , . interval = 0.1 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " y: {d:0.2} " , . { . value = & next . y , . min = min_y , . max = max_y , . interval = 0.1 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . position = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. angle = > | angle | {
var next = angle ;
if ( dvui . sliderEntry ( @src ( ) , " {d:0.2} rad " , . { . value = & next , . min = - std . math . pi * 2.0 , . max = std . math . pi * 2.0 , . interval = 0.01 } , . { . expand = . horizontal } ) ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . angle = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. scale = > | scale | {
var next = scale ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " x: {d:0.2} " , . { . value = & next . scale_x , . min = 0.0 , . max = 10.0 , . interval = 0.01 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " y: {d:0.2} " , . { . value = & next . scale_y , . min = 0.0 , . max = 10.0 , . interval = 0.01 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . scale = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. visible = > | v | {
var next = v ;
if ( dvui . checkbox ( @src ( ) , & next , " Visible " , . { } ) ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . visible = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. opacity = > | opacity | {
var next = opacity ;
if ( dvui . sliderEntry ( @src ( ) , " {d:0.2} " , . { . value = & next , . min = 0.0 , . max = 1.0 , . interval = 0.01 } , . { . expand = . horizontal } ) ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . opacity = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. locked = > | v | {
var next = v ;
if ( dvui . checkbox ( @src ( ) , & next , " Locked " , . { } ) ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . locked = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. size = > | size | {
var next = size ;
const doc = canvas . document ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " w: {d:0.2} " , . { . value = & next . w , . min = 0.0 , . max = doc . size . w , . interval = 1.0 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " h: {d:0.2} " , . { . value = & next . h , . min = 0.0 , . max = doc . size . h , . interval = 1.0 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . size = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. radii = > | radii | {
var next = radii ;
const doc = canvas . document ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " x: {d:0.2} " , . { . value = & next . x , . min = 0.0 , . max = doc . size . w , . interval = 1.0 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " y: {d:0.2} " , . { . value = & next . y , . min = 0.0 , . max = doc . size . h , . interval = 1.0 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . radii = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. end_point = > | pt | {
var next = pt ;
const doc = canvas . document ;
const min_x = - doc . size . w ;
const max_x = doc . size . w ;
const min_y = - doc . size . h ;
const max_y = doc . size . h ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " x: {d:0.2} " , . { . value = & next . x , . min = min_x , . max = max_x , . interval = 0.1 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " y: {d:0.2} " , . { . value = & next . y , . min = min_y , . max = max_y , . interval = 0.1 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . end_point = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
. points = > | points | {
dvui . label ( @src ( ) , " Points: {d} " , . { points . items . len } , . { } ) ;
} ,
. fill_rgba = > | rgba | {
drawColorEditor ( canvas , obj , rgba , true ) ;
} ,
. stroke_rgba = > | rgba | {
drawColorEditor ( canvas , obj , rgba , false ) ;
} ,
. thickness = > | t | {
var next = t ;
if ( dvui . sliderEntry ( @src ( ) , " {d:0.2} " , . { . value = & next , . min = 0.0 , . max = 100.0 , . interval = 0.1 } , . { . expand = . horizontal } ) ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . thickness = next } } ) catch { } ;
canvas . requestRedraw ( ) ;
}
} ,
}
}
row . deinit ( ) ;
}
fn drawColorEditor ( canvas : * Canvas , obj : * Document . Object , rgba : u32 , is_fill : bool ) void {
var comps = rgbaToComponents ( rgba ) ;
var changed = false ;
changed = dvui . sliderEntry ( @src ( ) , " r: {d:0.0} " , . { . value = & comps . r , . min = 0 , . max = 255 , . interval = 1 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " g: {d:0.0} " , . { . value = & comps . g , . min = 0 , . max = 255 , . interval = 1 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " b: {d:0.0} " , . { . value = & comps . b , . min = 0 , . max = 255 , . interval = 1 } , . { . expand = . horizontal } ) or changed ;
changed = dvui . sliderEntry ( @src ( ) , " a: {d:0.0} " , . { . value = & comps . a , . min = 0 , . max = 255 , . interval = 1 } , . { . expand = . horizontal } ) or changed ;
if ( changed ) {
const next = componentsToRgba ( comps ) ;
if ( is_fill ) {
obj . setProperty ( canvas . document . allocator , . { . data = . { . fill_rgba = next } } ) catch { } ;
} else {
obj . setProperty ( canvas . document . allocator , . { . data = . { . stroke_rgba = next } } ) catch { } ;
}
canvas . requestRedraw ( ) ;
}
}
fn propertyLabel ( tag : std . meta . Tag ( PropertyData ) ) [ ] const u8 {
return switch ( tag ) {
. position = > " Position " ,
. angle = > " Angle " ,
. scale = > " Scale " ,
. visible = > " Visible " ,
. opacity = > " Opacity " ,
. locked = > " Locked " ,
. size = > " Size " ,
. radii = > " Radii " ,
. end_point = > " End point " ,
. points = > " Points " ,
. fill_rgba = > " Fill color " ,
. stroke_rgba = > " Stroke color " ,
. thickness = > " Thickness " ,
} ;
}
const RgbaComponents = struct {
r : f32 ,
g : f32 ,
b : f32 ,
a : f32 ,
} ;
fn rgbaToComponents ( rgba : u32 ) RgbaComponents {
return . {
. r = @floatFromInt ( ( rgba > > 24 ) & 0xFF ) ,
. g = @floatFromInt ( ( rgba > > 16 ) & 0xFF ) ,
. b = @floatFromInt ( ( rgba > > 8 ) & 0xFF ) ,
. a = @floatFromInt ( ( rgba > > 0 ) & 0xFF ) ,
} ;
}
fn componentsToRgba ( comps : RgbaComponents ) u32 {
return ( @as ( u32 , toByte ( comps . r ) ) < < 24 ) |
( @as ( u32 , toByte ( comps . g ) ) < < 16 ) |
( @as ( u32 , toByte ( comps . b ) ) < < 8 ) |
( @as ( u32 , toByte ( comps . a ) ) < < 0 ) ;
}
fn toByte ( value : f32 ) u8 {
const clamped = std . math . clamp ( @round ( value ) , 0.0 , 255.0 ) ;
return @intFromFloat ( clamped ) ;
}