private static void BuildList(ObservableCollection <Lime.NodeComponent> source, Widget container) { var list = new Widget { Layout = new VBoxLayout { Spacing = 5 }, Padding = new Thickness { Top = AttachmentMetrics.Spacing }, }; container.AddNode(list); var validComponents = Project.Current.RegisteredComponentTypes .Where(t => NodeCompositionValidator.ValidateComponentType(typeof(Node3D), t)).ToList(); var widgetFactory = new AttachmentWidgetFactory <NodeComponent>(w => new NodeComponentRow(w, source), source); var footer = DeletableRow <NodeComponentRow> .CreateFooter(() => { var menu = new Menu(); foreach (var type in validComponents.Except(GetExceptedTypes(source))) { ICommand command = new Command(CamelCaseToLabel(type.Name), () => { var constructor = type.GetConstructor(Type.EmptyTypes); history.DoTransaction(() => Core.Operations.InsertIntoList.Perform( source, source.Count, constructor.Invoke(new object[] { }))); }); menu.Add(command); } menu.Popup(); }); footer.AddChangeWatcher(() => validComponents.Except(GetExceptedTypes(source)).Any(), any => footer.Visible = any); widgetFactory.AddFooter(footer); list.Components.Add(widgetFactory); }
public static void Perform(Node container, FolderItemLocation location, IFolderItem item) { if (item is Node && !NodeCompositionValidator.Validate(container.GetType(), item.GetType())) { throw new InvalidOperationException($"Can't put {item.GetType()} into {container.GetType()}"); } DocumentHistory.Current.Perform(new InsertFolderItem(container, location, item)); }
private void AddComponentsMenu(IReadOnlyList <Node> nodes, Widget widget) { if (nodes.Any(n => !string.IsNullOrEmpty(n.ContentsPath))) { return; } var nodesTypes = nodes.Select(n => n.GetType()).ToList(); var types = new List <Type>(); foreach (var type in Project.Current.RegisteredComponentTypes) { if ( !nodes.All(n => n.Components.Contains(type)) && nodesTypes.All(t => NodeCompositionValidator.ValidateComponentType(t, type)) ) { types.Add(type); } } types.Sort((a, b) => a.Name.CompareTo(b.Name)); var label = new Widget { LayoutCell = new LayoutCell { StretchY = 0 }, Layout = new HBoxLayout(), MinHeight = Theme.Metrics.DefaultButtonSize.Y, Nodes = { new ThemedAddButton { Clicked = () => { var menu = new Menu(); foreach (var type in types) { ICommand command = new Command(CamelCaseToLabel(type.Name), () => CreateComponent(type, nodes)); menu.Add(command); } menu.Popup(); }, Enabled = types.Count > 0 }, new ThemedSimpleText { Text = "Add Component", Padding = new Thickness(4, 0), VAlignment = VAlignment.Center, ForceUncutText = false, } } }; label.CompoundPresenter.Add(new WidgetFlatFillPresenter(ColorTheme.Current.Inspector.CategoryLabelBackground)); widget.AddNode(label); }
private void CreateContextMenu(List <string> files) { var menu = new Menu(); foreach (var imageType in imageTypes) { if (NodeCompositionValidator.Validate(Document.Current.Container.GetType(), imageType)) { menu.Add(new Command($"Create {imageType.Name}", () => CreateImageTypeInstance(imageType, files))); } } menu.Add(new Command("Create sprite animated Image", () => CreateSpriteAnimatedImage(files))); menu.Popup(); }
public override void ExecuteTransaction() { var nodes = Document.Current?.SelectedNodes().Editable().ToList(); if (nodes.Count != 1) { AlertDialog.Show("Please, select a single node"); return; } if (!(nodes[0] is Widget w && NodeCompositionValidator.CanHaveChildren(w.GetType()))) { AlertDialog.Show($"Can't export {nodes[0].GetType()}"); return; } Export(w); }
public static bool Perform(Node container, bool selectFirstNode = true) { if (!NodeCompositionValidator.CanHaveChildren(container.GetType())) { return(false); } if (!string.IsNullOrEmpty(container.ContentsPath) && Project.Current.DocumentExists(container.ContentsPath)) { OpenExternalScene(container.ContentsPath); } else { ChangeContainer(container, selectFirstNode); SetProperty.Perform(container, nameof(Node.TangerineFlags), container.TangerineFlags | TangerineFlags.DisplayContent, isChangingDocument: false); } return(true); }
public static bool Perform(Node container, bool selectFirstNode = true) { if (!NodeCompositionValidator.CanHaveChildren(container.GetType())) { return(false); } if (!string.IsNullOrEmpty(container.ContentsPath)) { OpenExternalScene(container.ContentsPath); } else { ChangeContainer(container, selectFirstNode); } container.SetTangerineFlag(TangerineFlags.DisplayContent, true); return(true); }
private static void Validate(Node source, Type destType, Type commonParent) { foreach (var child in source.Nodes) { if (!NodeCompositionValidator.Validate(destType, child.GetType())) { throw new InvalidOperationException( $"Node {source} has child {child} that will be incompatible with {destType}" ); } } foreach (var component in source.Components) { if (!NodeCompositionValidator.ValidateComponentType(destType, component.GetType())) { throw new InvalidOperationException( $"Node {source} has component {component} that will be incompatible with {destType}" ); } } if (!(source.GetType().IsSubclassOf(commonParent) && destType.IsSubclassOf(commonParent))) { throw new InvalidOperationException( $"Node {source} type or/and destination {destType} type are not subclasses of {commonParent}" ); } foreach (var animator in source.Animators) { var prop = destType.GetProperty(animator.TargetPropertyPath); if ( prop == null || prop.PropertyType != source.GetType().GetProperty(animator.TargetPropertyPath).PropertyType ) { throw new InvalidOperationException( $"Node {source} has animator on property {animator.TargetPropertyPath}, which doesn't exist in {destType}" ); } } }
public override void ExecuteTransaction() { var nodes = Document.Current?.SelectedNodes().Editable().ToList(); if (nodes.Count != 1) { AlertDialog.Show("Please, select a single node"); return; } if (!(nodes[0] is Widget w && NodeCompositionValidator.CanHaveChildren(w.GetType()) && Document.Current != null)) { AlertDialog.Show($"Can't inline {nodes[0].GetType()}"); return; } var node = nodes[0]; var clone = node.Clone(); clone.ContentsPath = null; var location = Document.Current.Container.RootFolder().Find(node); UnlinkFolderItem.Perform(Document.Current.Container, node); InsertFolderItem.Perform(Document.Current.Container, location, clone); }
public RollNodeView(Row row) { this.row = row; nodeData = row.Components.Get <NodeRow>(); label = new ThemedSimpleText { ForceUncutText = false, VAlignment = VAlignment.Center, OverflowMode = TextOverflowMode.Ellipsis, LayoutCell = new LayoutCell(Alignment.LeftCenter, float.MaxValue) }; editBoxContainer = new Widget { Visible = false, Layout = new HBoxLayout(), LayoutCell = new LayoutCell(Alignment.LeftCenter, float.MaxValue), }; nodeIcon = new Image(NodeIconPool.GetTexture(nodeData.Node.GetType())) { HitTestTarget = true, MinMaxSize = new Vector2(21, 16), Padding = new Thickness { Left = 5 } }; expandButton = CreateExpandButton(); var expandButtonContainer = new Widget { Layout = new StackLayout { IgnoreHidden = false }, LayoutCell = new LayoutCell(Alignment.Center, stretchX: 0), Nodes = { expandButton } }; expandButtonContainer.CompoundPresenter.Add(new SyncDelegatePresenter <Widget>(widget => { widget.PrepareRendererState(); var a = new Vector2(0, -4); var b = widget.Size + new Vector2(0, 3); int colorIndex = nodeData.Node.EditorState().ColorIndex; Renderer.DrawRect(a, b, ColorMarks[colorIndex]); if (colorIndex != 0) { Renderer.DrawRectOutline(a, b, ColorTheme.Current.TimelineRoll.Lines); } })); expandButtonContainer.Updating += delta => { bool visible = false; foreach (var a in nodeData.Node.Animators) { if (!a.IsZombie && a.AnimationId == Document.Current.AnimationId) { visible = true; break; } } expandButton.Visible = visible; }; enterButton = NodeCompositionValidator.CanHaveChildren(nodeData.Node.GetType()) ? CreateEnterButton() : null; eyeButton = CreateEyeButton(); lockButton = CreateLockButton(); lockAnimationButton = CreateLockAnimationButton(); widget = new Widget { HitTestTarget = true, Padding = new Thickness { Right = 2 }, MinHeight = TimelineMetrics.DefaultRowHeight, Layout = new HBoxLayout { DefaultCell = new DefaultLayoutCell(Alignment.Center) }, Nodes = { expandButtonContainer, (indentSpacer = new Widget()), nodeIcon, Spacer.HSpacer(3), label, editBoxContainer, linkIndicatorButtonContainer, (Widget)enterButton ?? (Widget)Spacer.HSpacer(Theme.Metrics.DefaultToolbarButtonSize.X), lockAnimationButton, eyeButton, lockButton, }, }; widget.Components.Add(new AwakeBehavior()); label.AddChangeWatcher(() => nodeData.Node.Id, s => RefreshLabel()); label.AddChangeWatcher(() => IsGrayedLabel(nodeData.Node), s => RefreshLabel()); label.AddChangeWatcher(() => nodeData.Node.ContentsPath, s => RefreshLabel()); widget.CompoundPresenter.Push(new SyncDelegatePresenter <Widget>(RenderBackground)); nodeIdEditor = new ObjectIdInplaceEditor(row, nodeData.Node, label, editBoxContainer); label.HitTestTarget = true; label.Gestures.Add(new DoubleClickGesture(() => { Document.Current.History.DoTransaction(() => { if (NodeData.Node.EditorState().Locked) { return; } var labelExtent = label.MeasureUncutText(); if (label.LocalMousePosition().X < labelExtent.X) { nodeIdEditor.Rename(); } else { EnterNode.Perform(nodeData.Node); } }); })); widget.Gestures.Add(new ClickGesture(1, ShowContextMenu)); }
static bool AllowChildren(PropertyEditorParams context) { return(context.Objects.All(o => NodeCompositionValidator.CanHaveChildren(o.GetType()))); }
public RollNodeView(Row row) { this.row = row; nodeData = row.Components.Get <NodeRow>(); label = new ThemedSimpleText { ForceUncutText = false, VAlignment = VAlignment.Center, LayoutCell = new LayoutCell(Alignment.LeftCenter, float.MaxValue) }; editBox = new ThemedEditBox { LayoutCell = new LayoutCell(Alignment.LeftCenter, float.MaxValue) }; nodeIcon = new Image(NodeIconPool.GetTexture(nodeData.Node.GetType())) { HitTestTarget = true, MinMaxSize = new Vector2(21, 16), Padding = new Thickness { Left = 5 } }; expandButton = CreateExpandButton(); var expandButtonContainer = new Widget { Layout = new StackLayout { IgnoreHidden = false }, LayoutCell = new LayoutCell(Alignment.Center, stretchX: 0), Nodes = { expandButton } }; expandButtonContainer.CompoundPresenter.Add(new DelegatePresenter <Widget>(widget => { widget.PrepareRendererState(); var a = new Vector2(0, -4); var b = widget.Size + new Vector2(0, 3); Renderer.DrawRect(a, b, nodeData.Node.GetColor()); if (nodeData.Node.GetColorIndex() != 0) { Renderer.DrawRectOutline(a, b, ColorTheme.Current.TimelineRoll.Lines); } })); expandButtonContainer.Updating += delta => expandButton.Visible = nodeData.Node.Animators.Count > 0; enterButton = NodeCompositionValidator.CanHaveChildren(nodeData.Node.GetType()) ? CreateEnterButton() : null; eyeButton = CreateEyeButton(); lockButton = CreateLockButton(); lockAnimationButton = CreateLockAnimationButton(); widget = new Widget { Padding = new Thickness { Right = 2 }, MinHeight = TimelineMetrics.DefaultRowHeight, Layout = new HBoxLayout { CellDefaults = new LayoutCell(Alignment.Center) }, HitTestTarget = true, Nodes = { expandButtonContainer, (indentSpacer = new Widget()), nodeIcon, new HSpacer(3), label, editBox, new Widget(), (Widget)enterButton ?? (Widget) new HSpacer(Theme.Metrics.DefaultToolbarButtonSize.X), lockAnimationButton, eyeButton, lockButton, } }; label.AddChangeWatcher(() => nodeData.Node.Id, s => RefreshLabel()); label.AddChangeWatcher(() => IsGrayedLabel(nodeData.Node), s => RefreshLabel()); label.AddChangeWatcher(() => nodeData.Node.ContentsPath, s => RefreshLabel()); widget.CompoundPresenter.Push(new DelegatePresenter <Widget>(RenderBackground)); editBox.Visible = false; widget.Gestures.Add(new ClickGesture(1, ShowPropertyContextMenu)); widget.Gestures.Add(new DoubleClickGesture(() => { var labelExtent = label.MeasureUncutText(); if (label.LocalMousePosition().X < labelExtent.X) { Rename(); } else { Core.Operations.EnterNode.Perform(nodeData.Node); } })); nodeIcon.Gestures.Add(new DoubleClickGesture(() => { Core.Operations.ClearRowSelection.Perform(); Core.Operations.SelectRow.Perform(row); Rename(); })); }
private void Handle(IEnumerable <string> files) { Handling?.Invoke(); using (Document.Current.History.BeginTransaction()) { pendingImages = new List <string>(); foreach (var file in files) { try { string assetPath, assetType; if (!Utils.ExtractAssetPathOrShowAlert(file, out assetPath, out assetType) || !Utils.AssertCurrentDocument(assetPath, assetType)) { continue; } var nodeCreatingEventArgs = new NodeCreatingEventArgs(assetPath, assetType); NodeCreating?.Invoke(nodeCreatingEventArgs); if (nodeCreatingEventArgs.Cancel) { continue; } var fileName = Path.GetFileNameWithoutExtension(assetPath); switch (assetType) { case ".png": pendingImages.Add(assetPath); break; case ".ogg": { var node = CreateNode.Perform(typeof(Audio)); var sample = new SerializableSample(assetPath); SetProperty.Perform(node, nameof(Audio.Sample), sample); SetProperty.Perform(node, nameof(Node.Id), fileName); SetProperty.Perform(node, nameof(Audio.Volume), 1); var key = new Keyframe <AudioAction> { Frame = Document.Current.AnimationFrame, Value = AudioAction.Play }; SetKeyframe.Perform(node, nameof(Audio.Action), Document.Current.AnimationId, key); OnNodeCreated(node); break; } case ".tan": case ".model": case ".scene": DropSceneContextMenu.Create(assetPath, assetType, NodeCreated); break; } } catch (System.Exception e) { AlertDialog.Show(e.Message); } } if (pendingImages.Count > 0) { var menu = new Menu(); foreach (var kv in imageDropCommands.Commands) { if (NodeCompositionValidator.Validate(Document.Current.Container.GetType(), kv.Value)) { menu.Add(kv.Key); } } menu.Popup(); } Document.Current.History.CommitTransaction(); } }
public static bool PasteNodes(string data, RowLocation location, Vector2?mousePosition) { bool CanPaste() { // We are support only paste into folders for now. return(location.ParentRow.Components.Contains <FolderRow>() || location.ParentRow.Components.Contains <BoneRow>()); } if (!CanPaste()) { return(false); } Frame frame; try { var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(data)); frame = TangerinePersistence.Instance.ReadObject <Frame>(Document.Current.Path, stream); } catch (System.Exception e) { Debug.Write(e); return(false); } var animators = frame.Animators; var items = frame.RootFolder().Items.Where(item => NodeCompositionValidator.IsCopyPasteAllowed(item.GetType())).ToList(); if (items.Count == 0) { if (animators.Count != 0) { foreach (var row in Document.Current.TopLevelSelectedRows().ToList()) { if (!(row.Components.Get <NodeRow>()?.Node is IAnimationHost animable)) { continue; } Document.Current.History.DoTransaction(() => { foreach (var animator in animators) { if (animable.GetType().GetProperty(animator.TargetPropertyPath) == null) { continue; } foreach (var keyframe in animator.Keys) { SetKeyframe.Perform(animable, animator.TargetPropertyPath, animator.AnimationId, keyframe); } } }); } } return(true); } var folderLocation = location.ParentRow.Rows.Count > 0 ? Row.GetFolderItemLocation(location.ParentRow.Rows[location.Index]) : new FolderItemLocation { Index = 0, Folder = location.ParentRow.Components.Get <FolderRow>().Folder }; if (!folderLocation.Folder.Expanded) { SetProperty.Perform(folderLocation.Folder, nameof(Folder.Expanded), true); } mousePosition *= Document.Current.Container.AsWidget?.LocalToWorldTransform.CalcInversed(); var shift = mousePosition - items.OfType <Widget>().FirstOrDefault()?.Position; foreach (var n in items.OfType <Node>()) { Document.Current.Decorate(n); } if (shift.HasValue) { foreach (var w in items.OfType <Widget>()) { w.Position += shift.Value; } } frame.RootFolder().Items.Clear(); frame.RootFolder().SyncDescriptorsAndNodes(frame); ClearRowSelection.Perform(); while (items.Count > 0) { var item = items.First(); if (item is Bone bone) { if (bone.BaseIndex != 0) { continue; } var newIndex = 1; var bones = Document.Current.Container.Nodes.OfType <Bone>(); if (bones.Any()) { newIndex = bones.Max(b => b.Index) + 1; } var children = BoneUtils.FindBoneDescendats(bone, items.OfType <Bone>()).ToList(); var map = new Dictionary <int, int> { { bone.Index, newIndex } }; bone.BaseIndex = location.ParentRow.Components.Get <BoneRow>()?.Bone.Index ?? 0; bone.Index = newIndex; InsertFolderItem.Perform( Document.Current.Container, folderLocation, bone); folderLocation.Index++; foreach (var b in children) { b.BaseIndex = map[b.BaseIndex]; map.Add(b.Index, b.Index = ++newIndex); InsertFolderItem.Perform( Document.Current.Container, folderLocation, b); folderLocation.Index++; items.Remove(b); } Document.Current.Container.RootFolder().SyncDescriptorsAndNodes(Document.Current.Container); SortBonesInChain.Perform(bone); SelectRow.Perform(Document.Current.GetRowForObject(item)); } else { if (!location.ParentRow.Components.Contains <BoneRow>()) { InsertFolderItem.Perform( Document.Current.Container, folderLocation, item); folderLocation.Index++; SelectRow.Perform(Document.Current.GetRowForObject(item)); } } items.Remove(item); } return(true); }