/// <summary> /// Used when moving a node, to create new parent (this) stack or tab for it and target. /// Also when moving a node, to create new parent (this) tab for it (target). /// </summary> _Node(_Node target, bool isTab, bool verticalStack = false) { _pm = target._pm; bool targetIsRoot = !isTab && target.Parent == null; if (targetIsRoot) { _pm._rootStack = this; } else { _index = target._index; target.AddSibling(this, after: false); target.Remove(); target._index = 0; } AddChild(target); if (isTab) { _tab = new(); _elem = _tab.tc = new _TabControl(); } else { _stack = new _StackFields { isVertical = verticalStack }; _elem = _stack.grid = new Grid(); } _elem.Tag = this; if (targetIsRoot) { //target._dockedSize = ...; //_AddToParentWhenMovingOrAddingLater will set it for target and this _pm._setContainer(_stack.grid); } else { _ReplaceInStack(target); if (isTab) { _captionAt = target._captionAt; _InitTabControl(); } } }
/// <summary> /// Used when creating new leaf node later (after loading). /// </summary> _Node(_Node target, bool after, LeafType type, string name, bool canClose) { _pm = target._pm; _leaf = new() { addedLater = true, name = name, canClose = canClose }; _elem = _leaf.panel = new() { Tag = this, UseLayoutRounding = true }; _leafType = type; _dontSave = true; _Dictionary.Add(name, this); _AddToParentWhenMovingOrAddingLater(target, after); } public void Save(XmlWriter x) { if (Parent == null) //mark to not save stack/tab nodes without savable leaf descendants { _Children(this);
/// <summary> /// Used to create nodes when loading from XML. /// </summary> _Node(KPanels pm, XElement x, _Node parent, int index) { _pm = pm; _index = index; string tag = x.Name.LocalName; if (parent == null) //the root XML element { if (tag != "stack") { throw new ArgumentException("XML root element must be 'stack'"); } _pm._rootStack = this; } else { parent.AddChild(this); } switch (tag) { case "stack": _stack = new _StackFields { isVertical = x.Attr("o") == "v" }; _elem = _stack.grid = new Grid(); break; case "tab": _tab = new(); _elem = _tab.tc = new _TabControl(); break; case "panel": case "toolbar": case "document": _leaf = new(); _elem = _leaf.panel = new(); _leafType = tag[0] switch { 'p' => LeafType.Panel, 't' => LeafType.Toolbar, _ => LeafType.Document }; _leaf.name = x.Attr("name") ?? throw new ArgumentException("XML element without 'name'"); _Dictionary.Add(_leaf.name, this); break; default: throw new ArgumentException("unknown XML tag"); } _elem.UseLayoutRounding = true; _elem.Tag = this; if (parent != null) { if (!_IsDocument) { _savedDockState = (_DockState)(x.Attr("state", 0) & 3); _floatSavedRect = x.Attr("floatRect"); } if (!_IsStack) { x.Attr(out _captionAt, "captionAt"); } if (_ParentIsStack) { _dockedSize = _GridLengthFromString(x.Attr("z")); //height in vertical stack or width in horizontal stack _AddToStack(moving: false, _index == 0 ? 0 : x.Attr("s", c_defaultSplitterSize)); var flags = (_Flags)x.Attr("flags", 0); if (flags.Has(_Flags.Splitter_ResizeNearest) && _splitter != null) { _splitter.ResizeNearest = true; } if (_IsTab) { _InitTabControl(); } } else { _AddToTab(moving: false); } } if (!_IsLeaf) //stack or tab { if (_ParentIsTab) { throw new ArgumentException(tag + " in 'tab'"); } int i = 0; foreach (var e in x.Elements()) { new _Node(_pm, e, this, i++); } Debug.Assert(i > 0); if (_IsTab && i > 0) { _tab.tc.SelectedIndex = Math.Clamp(x.Attr("active", 0), 0, i - 1); } } //print.it(new string('\t', parent?.Level ?? 0) + _ntype, _ptype, Name, _indexInParent); if (parent == null) //the root XML element { int nVisible = 0; _Node firstHidden = null; List <_Node> aFloat = null; foreach (var v in Descendants()) { if (!v._IsStack) { if (v._IsVisibleReally(true)) { nVisible++; } else { firstHidden ??= v; } } var ds = v._savedDockState; if (ds == _DockState.Float) { (aFloat ??= new()).Add(v); } else if (ds != 0) { v._SetDockState(ds); } } if (nVisible == 0 && firstHidden != null) //if all non-stack hidden, unhide one, else user cannot unhide any because there are no captions to show the context menu { firstHidden._SetDockState(0); } if (aFloat != null) { DependencyPropertyChangedEventHandler eh = null; eh = (_, e) => { if (e.NewValue is bool visible && visible) { _stack.grid.IsVisibleChanged -= eh; _stack.grid.Dispatcher.InvokeAsync(() => { foreach (var v in aFloat) { v._SetDockState(_DockState.Float); } }); } }; _stack.grid.IsVisibleChanged += eh; } //timer.after(1000, _ => _Test(5)); ////timer.after(5000, _ => _Test(0)); //void _Test(int margin) { // foreach (var v in Descendants(true)) { // if (v._IsStack) v._stack.grid.Background = (v.Level & 3) switch { 0 => Brushes.CornflowerBlue, 1 => Brushes.Khaki, 2 => Brushes.YellowGreen, _ => Brushes.LightYellow }; // if(v!=this) v._elem.Margin = new Thickness(margin); // if (v._splitter != null) v._splitter.Visibility = Visibility.Collapsed; // } //} ////_stack.grid.PreviewMouseMove += _RootGrid_PreviewMouseMove; } }
/// <summary> /// Used to create root node when loading from XML. /// </summary> public _Node(KPanels pm, XElement x) : this(pm, x, null, 0) { }