mirror of
https://github.com/penpot/penpot.git
synced 2026-10-04 09:46:15 +00:00
* ✨ Adds static dispatch safe stubs in tests * 🐛 Fix shapesColors metadata key to match ColorShapeInfo * 🐛 Fix CommentThread.remove rejecting the owner's own threads * 🐛 Fix page.removeCommentThread throwing on a spurious Promise * ✨ Implement ShapeBase.swapComponent in the plugin API * ✨ Expose File.revn in the plugin API * 🐛 Fix FileVersion.createdAt calling Luxon method on a js/Date * 🐛 Fix plugin font/typography application to text and ranges * 🐛 Default plugin overlay interaction position for non-manual types * 🐛 Fix plugin interaction setters passing an id-only shape * 🐛 Fix grid addColumnAtIndex rejecting valid track types * 🐛 Expose libraryId on library color/typography/component proxies * ✨ Implement LibraryTypography.setFont in the plugin API * 🐛 Fix typography.applyToTextRange reading unexposed range bounds * 🐛 Fix utils.geometry.center argument mismatch * 🐛 Fix localStorage.removeItem calling getItem * 🐛 Fix shape backgroundBlur proxy key casing * 🐛 Report boolean shape type as 'boolean' in the plugin API * 🐛 Return the resulting paths from plugin flatten * 🐛 Make plugin z-order methods act on the target shape * 🐛 Make is-variant-container? return a boolean * ✨ Implement Group.isMask in the plugin API * 🐛 Return a shape proxy from TextRange.shape * 🐛 Return the duplicated set from TokenSet.duplicate * 🐛 Fix theme addSet/removeSet reading set name with a keyword * 🐛 Accept string fontFamilies token value in the plugin API * 🐛 Fix combineAsVariants ignoring the passed component ids * 🐛 Fix board removeRulerGuide ignoring its argument * 🐛 Fix board guides setter schema and parser * 🐛 Avoid 0-byte allocation when syncing empty grid tracks * 🐛 Validate grid track indices in the plugin API * 🐛 Return null for empty input in group() and centerShapes() * 🐛 Return TokenTypographyValue[] from a typography token's resolvedValue * 🐛 Return TokenShadowValue[] from a shadow token's resolvedValue * 🐛 Return string[] from a fontFamilies token's resolvedValue * 🐛 Clear mutually-exclusive reps when setting LibraryColor gradient/image * 🐛 Add readonly tags to types, deprecate Image type * 📚 Update plugins changelog
272 lines
6.9 KiB
Rust
272 lines
6.9 KiB
Rust
use macros::{wasm_error, ToJs};
|
|
|
|
use crate::get_render_state;
|
|
use crate::mem;
|
|
use crate::shapes::{GridCell, GridDirection, GridTrack, GridTrackType};
|
|
use crate::uuid::Uuid;
|
|
use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state};
|
|
|
|
use super::align;
|
|
|
|
#[allow(unused_imports)]
|
|
use crate::error::{Error, Result};
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C, align(1))]
|
|
struct RawGridCell {
|
|
row: i32,
|
|
row_span: i32,
|
|
column: i32,
|
|
column_span: i32,
|
|
align_self: u8,
|
|
justify_self: u8,
|
|
_padding: u16,
|
|
shape_id_a: u32,
|
|
shape_id_b: u32,
|
|
shape_id_c: u32,
|
|
shape_id_d: u32,
|
|
}
|
|
|
|
impl From<[u8; size_of::<RawGridCell>()]> for RawGridCell {
|
|
fn from(bytes: [u8; size_of::<RawGridCell>()]) -> Self {
|
|
unsafe { std::mem::transmute(bytes) }
|
|
}
|
|
}
|
|
|
|
impl From<RawGridCell> for GridCell {
|
|
fn from(raw: RawGridCell) -> Self {
|
|
let raw_justify_self = super::align::RawJustifySelf::from(raw.justify_self);
|
|
let raw_align_self = super::align::RawAlignSelf::from(raw.align_self);
|
|
let shape_id = uuid_from_u32_quartet(
|
|
raw.shape_id_a,
|
|
raw.shape_id_b,
|
|
raw.shape_id_c,
|
|
raw.shape_id_d,
|
|
);
|
|
|
|
Self {
|
|
row: raw.row,
|
|
row_span: raw.row_span,
|
|
column: raw.column,
|
|
column_span: raw.column_span,
|
|
align_self: raw_align_self.try_into().ok(),
|
|
justify_self: raw_justify_self.try_into().ok(),
|
|
shape: if shape_id != Uuid::nil() {
|
|
Some(shape_id)
|
|
} else {
|
|
None
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, ToJs)]
|
|
#[repr(u8)]
|
|
#[allow(dead_code)]
|
|
pub enum RawGridDirection {
|
|
Row = 0,
|
|
Column = 1,
|
|
}
|
|
|
|
impl From<u8> for RawGridDirection {
|
|
fn from(value: u8) -> Self {
|
|
unsafe { std::mem::transmute(value) }
|
|
}
|
|
}
|
|
|
|
impl From<RawGridDirection> for GridDirection {
|
|
fn from(value: RawGridDirection) -> Self {
|
|
match value {
|
|
RawGridDirection::Row => Self::Row,
|
|
RawGridDirection::Column => Self::Column,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, ToJs)]
|
|
#[repr(u8)]
|
|
#[allow(dead_code)]
|
|
pub enum RawGridTrackType {
|
|
Percent = 0,
|
|
Flex = 1,
|
|
Auto = 2,
|
|
Fixed = 3,
|
|
}
|
|
|
|
impl From<u8> for RawGridTrackType {
|
|
fn from(value: u8) -> Self {
|
|
unsafe { std::mem::transmute(value) }
|
|
}
|
|
}
|
|
|
|
impl From<RawGridTrackType> for GridTrackType {
|
|
fn from(value: RawGridTrackType) -> Self {
|
|
match value {
|
|
RawGridTrackType::Percent => Self::Percent,
|
|
RawGridTrackType::Flex => Self::Flex,
|
|
RawGridTrackType::Auto => Self::Auto,
|
|
RawGridTrackType::Fixed => Self::Fixed,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C, align(4))]
|
|
#[allow(dead_code)]
|
|
pub struct RawGridTrack {
|
|
track_type: RawGridTrackType,
|
|
_padding: [u8; 3],
|
|
value: f32,
|
|
}
|
|
|
|
impl From<[u8; size_of::<RawGridTrack>()]> for RawGridTrack {
|
|
fn from(bytes: [u8; size_of::<RawGridTrack>()]) -> Self {
|
|
unsafe { std::mem::transmute(bytes) }
|
|
}
|
|
}
|
|
|
|
impl From<RawGridTrack> for GridTrack {
|
|
fn from(raw: RawGridTrack) -> Self {
|
|
Self {
|
|
track_type: raw.track_type.into(),
|
|
value: raw.value,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn set_grid_layout_data(
|
|
dir: u8,
|
|
row_gap: f32,
|
|
column_gap: f32,
|
|
align_items: u8,
|
|
align_content: u8,
|
|
justify_items: u8,
|
|
justify_content: u8,
|
|
padding_top: f32,
|
|
padding_right: f32,
|
|
padding_bottom: f32,
|
|
padding_left: f32,
|
|
) {
|
|
let dir = RawGridDirection::from(dir);
|
|
let align_items = align::RawAlignItems::from(align_items);
|
|
let align_content = align::RawAlignContent::from(align_content);
|
|
let justify_items = align::RawJustifyItems::from(justify_items);
|
|
let justify_content = align::RawJustifyContent::from(justify_content);
|
|
|
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
|
shape.set_grid_layout_data(
|
|
dir.into(),
|
|
row_gap,
|
|
column_gap,
|
|
align_items.into(),
|
|
align_content.into(),
|
|
justify_items.into(),
|
|
justify_content.into(),
|
|
padding_top,
|
|
padding_right,
|
|
padding_bottom,
|
|
padding_left,
|
|
);
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
pub extern "C" fn set_grid_columns() -> Result<()> {
|
|
let bytes = mem::bytes_or_empty();
|
|
|
|
let entries: Vec<GridTrack> = bytes
|
|
.chunks(size_of::<RawGridTrack>())
|
|
.map(|data| {
|
|
let track_bytes: [u8; size_of::<RawGridTrack>()] = data
|
|
.try_into()
|
|
.map_err(|_| Error::CriticalError("Invalid bytes for grid track".to_string()))?;
|
|
Ok(RawGridTrack::from(track_bytes).into())
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
|
shape.set_grid_columns(entries);
|
|
});
|
|
|
|
mem::free_bytes()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
pub extern "C" fn set_grid_rows() -> Result<()> {
|
|
let bytes = mem::bytes_or_empty();
|
|
|
|
let entries: Vec<GridTrack> = bytes
|
|
.chunks(size_of::<RawGridTrack>())
|
|
.map(|data| {
|
|
let track_bytes: [u8; size_of::<RawGridTrack>()] = data
|
|
.try_into()
|
|
.map_err(|_| Error::CriticalError("Invalid bytes for grid track".to_string()))?;
|
|
Ok(RawGridTrack::from(track_bytes).into())
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
|
shape.set_grid_rows(entries);
|
|
});
|
|
|
|
mem::free_bytes()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
pub extern "C" fn set_grid_cells() -> Result<()> {
|
|
let bytes = mem::bytes_or_empty();
|
|
|
|
let cells: Vec<RawGridCell> = bytes
|
|
.chunks(size_of::<RawGridCell>())
|
|
.map(|data| {
|
|
let cell_bytes: [u8; size_of::<RawGridCell>()] = data
|
|
.try_into()
|
|
.map_err(|_| Error::CriticalError("Invalid bytes for grid cell".to_string()))?;
|
|
Ok(RawGridCell::from(cell_bytes))
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
|
shape.set_grid_cells(cells.into_iter().map(|raw| raw.into()).collect());
|
|
});
|
|
|
|
mem::free_bytes()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn show_grid(a: u32, b: u32, c: u32, d: u32) {
|
|
let id = uuid_from_u32_quartet(a, b, c, d);
|
|
get_render_state().show_grid = Some(id);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn hide_grid() {
|
|
get_render_state().show_grid = None;
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn get_grid_coords(pos_x: f32, pos_y: f32) -> *mut u8 {
|
|
let row: i32;
|
|
let col: i32;
|
|
with_state!(state, {
|
|
if let Some((r, c)) = state.get_grid_coords(pos_x, pos_y) {
|
|
row = r;
|
|
col = c;
|
|
} else {
|
|
row = -1;
|
|
col = -1;
|
|
};
|
|
});
|
|
let mut bytes = vec![0; 8];
|
|
bytes[0..4].clone_from_slice(&row.to_le_bytes());
|
|
bytes[4..8].clone_from_slice(&col.to_le_bytes());
|
|
mem::write_bytes(bytes)
|
|
}
|