Checkpoint source 7 workflows and in-progress diagram authoring redesign
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Preserve current core, source, REST, desktop, recovery, documentation and validation work, including AGENTS.md and the development summary. This is a work-in-progress checkpoint: the new compact palette has outstanding compilation errors and native acceptance; earlier passing gates retain their documented scope.
This commit is contained in:
@@ -30,6 +30,9 @@ pub enum CanvasEvent {
|
||||
Scrolled(Context, scenario::ScrollArea, iced::Vector),
|
||||
Choose(Context, CanvasTool),
|
||||
Select(Context, Id),
|
||||
SelectQualified(Context, OccurrenceEndpoint),
|
||||
ExpandQualified(Context, OccurrenceEndpoint),
|
||||
OpenQualified(Context, OccurrenceEndpoint),
|
||||
AppendExchange(Context),
|
||||
Begin {
|
||||
context: Context,
|
||||
@@ -57,6 +60,7 @@ pub enum CanvasEvent {
|
||||
Open(Context, Id),
|
||||
Back(Context),
|
||||
Zoom(Context, Option<f32>),
|
||||
Focus(Context),
|
||||
Viewport(Context, Size),
|
||||
}
|
||||
impl CanvasEvent {
|
||||
@@ -65,11 +69,15 @@ impl CanvasEvent {
|
||||
Self::Scrolled(c, ..)
|
||||
| Self::Choose(c, ..)
|
||||
| Self::Select(c, ..)
|
||||
| Self::SelectQualified(c, ..)
|
||||
| Self::ExpandQualified(c, ..)
|
||||
| Self::OpenQualified(c, ..)
|
||||
| Self::Expand(c, ..)
|
||||
| Self::Open(c, ..)
|
||||
| Self::Back(c)
|
||||
| Self::AppendExchange(c)
|
||||
| Self::Zoom(c, ..)
|
||||
| Self::Focus(c)
|
||||
| Self::Viewport(c, ..) => *c,
|
||||
Self::Begin { context, .. }
|
||||
| Self::Connect { context, .. }
|
||||
@@ -79,6 +87,53 @@ impl CanvasEvent {
|
||||
}
|
||||
}
|
||||
impl State {
|
||||
pub(crate) fn select_context(&mut self, address: oa_core::topology_context::ContextAddress) {
|
||||
self.selected_context = Some(address);
|
||||
}
|
||||
pub(crate) fn clear_context(&mut self) {
|
||||
self.selected_context = None;
|
||||
}
|
||||
pub(super) fn qualified_selection(
|
||||
&self,
|
||||
p: &Project,
|
||||
owner: Id,
|
||||
) -> Option<&oa_core::topology_context::ContextAddress> {
|
||||
self.selected_context.as_ref().filter(|address| {
|
||||
address.owner == owner
|
||||
&& oa_core::topology_context::TopologyIndex::new(p)
|
||||
.resolve(owner, &address.endpoint)
|
||||
.is_ok()
|
||||
})
|
||||
}
|
||||
pub(crate) fn expand_context(
|
||||
&mut self,
|
||||
p: &Project,
|
||||
owner: Id,
|
||||
endpoint: OccurrenceEndpoint,
|
||||
) -> bool {
|
||||
if oa_core::topology_context::TopologyIndex::new(p)
|
||||
.resolve(owner, &endpoint)
|
||||
.is_err()
|
||||
|| !matches!(
|
||||
p.elements
|
||||
.get(&endpoint.occurrence)
|
||||
.and_then(|e| e.shared()),
|
||||
Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::SequenceReference { sequence: Some(_) },
|
||||
..
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if let Some(root) = endpoint.references.first() {
|
||||
self.expanded.insert(*root);
|
||||
}
|
||||
if !self.contextual_expanded.remove(&endpoint) {
|
||||
self.contextual_expanded.insert(endpoint);
|
||||
}
|
||||
true
|
||||
}
|
||||
pub(crate) fn enter(&mut self, document: Id, owner: Id) {
|
||||
if self.identity != Some((document, owner)) {
|
||||
self.identity = Some((document, owner));
|
||||
@@ -90,6 +145,7 @@ impl State {
|
||||
self.scroll_offsets = [iced::Vector::ZERO; 2];
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) fn scroll_offset(&self) -> iced::Vector {
|
||||
self.scroll_offsets[0] + self.scroll_offsets[1]
|
||||
}
|
||||
@@ -133,11 +189,18 @@ impl State {
|
||||
true
|
||||
}
|
||||
pub(crate) fn remember(&mut self, owner: Id, occurrence: Id) {
|
||||
self.return_to.push((owner, occurrence));
|
||||
self.return_to
|
||||
.push((owner, occurrence, self.selected_context.clone()));
|
||||
}
|
||||
pub(crate) fn back(&mut self, p: &Project) -> Option<(Id, Id)> {
|
||||
while let Some((owner, occurrence)) = self.return_to.pop() {
|
||||
while let Some((owner, occurrence, address)) = self.return_to.pop() {
|
||||
if is_contribution(p, owner, occurrence) {
|
||||
self.selected_context = address.filter(|address| {
|
||||
address.owner == owner
|
||||
&& oa_core::topology_context::TopologyIndex::new(p)
|
||||
.resolve(owner, &address.endpoint)
|
||||
.is_ok()
|
||||
});
|
||||
return Some((owner, occurrence));
|
||||
}
|
||||
}
|
||||
@@ -145,6 +208,11 @@ impl State {
|
||||
}
|
||||
pub(crate) fn created_selection(&self, command: &Command) -> Option<Id> {
|
||||
let owner = self.pending_owner()?;
|
||||
if let Command::CreateRelation(r) | Command::EditRelation(_, r) = command
|
||||
&& matches!(&r.body, RelationBody::ContextualLink { owner: caller, .. } if *caller == owner)
|
||||
{
|
||||
return Some(r.id);
|
||||
}
|
||||
let Command::Batch(commands) = command else {
|
||||
return None;
|
||||
};
|
||||
@@ -355,6 +423,20 @@ impl Graph<'_> {
|
||||
.find(|n| n.rect.contains(point))
|
||||
.map(|n| n.id)
|
||||
}
|
||||
fn qualified_at(&self, point: Point) -> Option<OccurrenceEndpoint> {
|
||||
self.nodes.iter().find_map(|node| {
|
||||
node.preview.iter().enumerate().find_map(|(i, (id, _))| {
|
||||
let rect = Rectangle::new(
|
||||
Point::new(
|
||||
node.rect.x + 12. + (i % 2) as f32 * 125.,
|
||||
node.rect.y + 118. + (i / 2) as f32 * 44.,
|
||||
),
|
||||
Size::new(113., 32.),
|
||||
);
|
||||
rect.contains(point).then(|| id.clone())
|
||||
})
|
||||
})
|
||||
}
|
||||
fn edge_at(&self, point: Point) -> Option<Id> {
|
||||
self.edges
|
||||
.iter()
|
||||
@@ -366,6 +448,19 @@ impl Graph<'_> {
|
||||
})
|
||||
.map(|e| e.id)
|
||||
}
|
||||
fn contextual_edge_at(&self, point: Point) -> Option<Id> {
|
||||
self.edges
|
||||
.iter()
|
||||
.filter(|edge| edge.layer == EdgeLayer::Contextual)
|
||||
.find(|edge| {
|
||||
edge.label.contains(point)
|
||||
|| edge.points.windows(2).any(|segment| {
|
||||
crate::diagram::distance_to_segment(point, segment[0], segment[1])
|
||||
< 8. / self.scale
|
||||
})
|
||||
})
|
||||
.map(|edge| edge.id)
|
||||
}
|
||||
fn handle(&self, id: Id) -> Option<Rectangle> {
|
||||
let n = self.nodes.iter().find(|n| n.id == id)?;
|
||||
Some(Rectangle::new(
|
||||
@@ -374,6 +469,9 @@ impl Graph<'_> {
|
||||
))
|
||||
}
|
||||
fn endpoint_at(&self, point: Point) -> Option<(Id, End)> {
|
||||
if self.project.relations.get(&self.selected)?.kind() != RelationKind::SequenceLink {
|
||||
return None;
|
||||
}
|
||||
let e = self.edges.iter().find(|e| e.id == self.selected)?;
|
||||
[(e.points[0], End::Source), (*e.points.last()?, End::Target)]
|
||||
.into_iter()
|
||||
@@ -497,13 +595,17 @@ impl Graph<'_> {
|
||||
});
|
||||
}
|
||||
CanvasTool::Select => {
|
||||
if let Some((link, end)) = self.endpoint_at(point) {
|
||||
if let Some(endpoint) = self.qualified_at(point) {
|
||||
return publish(CanvasEvent::SelectQualified(context, endpoint));
|
||||
} else if let Some((link, end)) = self.endpoint_at(point) {
|
||||
state.press = Some(Press::Reconnect {
|
||||
link,
|
||||
end,
|
||||
start,
|
||||
moved: false,
|
||||
});
|
||||
} else if let Some(link) = self.contextual_edge_at(point) {
|
||||
return publish(CanvasEvent::Select(context, link));
|
||||
} else if self
|
||||
.handle(self.selected)
|
||||
.is_some_and(|r| r.contains(point))
|
||||
@@ -728,13 +830,17 @@ impl State {
|
||||
owner: Id,
|
||||
id: Id,
|
||||
) -> Option<scrollable::AbsoluteOffset> {
|
||||
let graph = Graph::expanded(p, owner, id, &self.expanded);
|
||||
let graph =
|
||||
Graph::expanded_qualified(p, owner, id, &self.expanded, &self.contextual_expanded);
|
||||
let scale = self.scale(graph.size, 500.);
|
||||
let rect = graph
|
||||
.nodes
|
||||
.iter()
|
||||
.find(|n| n.id == id)
|
||||
.map(|n| n.rect)
|
||||
.map(|n| {
|
||||
self.qualified_selection(p, owner)
|
||||
.map_or(n.rect, |a| n.endpoint_rect(&a.endpoint))
|
||||
})
|
||||
.or_else(|| graph.edges.iter().find(|e| e.id == id).map(|e| e.label))?;
|
||||
Some(scrollable::AbsoluteOffset {
|
||||
x: (rect.x * scale - 12.).max(0.),
|
||||
|
||||
Reference in New Issue
Block a user