#pragma warning disable 168 using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Text; using System.Threading.Tasks; using Beefy; using Beefy.gfx; using Beefy.widgets; using Beefy.theme.dark; using Beefy.events; using Beefy.geom; using System.IO; namespace IDE.ui { public class SearchEditWidget : DarkEditWidget { public int mSearchVersion; protected override void SetupInsets() { mScrollContentInsets.Set(GS!(3), GS!(23), GS!(3), GS!(3)); } } public class KeysEditWidgetContent : DarkEditWidgetContent { public override void MouseDown(float x, float y, int32 btn, int32 btnCount) { // Disable } } public class KeysEditWidget : DarkEditWidget { public this() : base(new KeysEditWidgetContent()) { mEditWidgetContent.mEditWidget = this; mOnKeyDown.Add(new => KeyDownEvent); } void KeyDownEvent(KeyDownEvent evt) { if ((evt.mKeyCode == .Control) || (evt.mKeyCode == .Shift) || (evt.mKeyCode == .Alt) || (evt.mKeyCode == .RAlt)) return; if ((evt.mKeyFlags.HeldKeys == 0) && (evt.mKeyCode == .Escape)) { evt.mHandled = false; return; } var keyState = scope KeyState(); keyState.mKeyCode = evt.mKeyCode; keyState.mKeyFlags = evt.mKeyFlags.HeldKeys; if (keyState.mKeyFlags.HeldKeys == 0) { if (keyState.mKeyCode == .Return) return; } evt.mHandled = true; var str = scope String(); keyState.ToString(str); if (HasKeys) { str.Insert(0, ", "); mEditWidgetContent.mIsReadOnly = false; Content.InsertAtCursor(str); mEditWidgetContent.mIsReadOnly = true; } else { if ((evt.mKeyFlags.HeldKeys == 0) && ((evt.mKeyCode == .Delete) || (evt.mKeyCode == .Backspace)) && ((Content.HasSelection()) || (!HasKeys))) { if ((evt.mKeyCode == .Delete) || (evt.mKeyCode == .Backspace)) { Content.ClearText(); return; } } mEditWidgetContent.mIsReadOnly = false; Content.InsertAtCursor(str); mEditWidgetContent.mIsReadOnly = true; } } public override void MouseDown(float x, float y, int32 btn, int32 btnCount) { base.MouseDown(x, y, btn, btnCount); } public bool HasKeys { get { var str = scope String(); GetText(str); return !str.IsEmpty && !str.StartsWith("<"); } } } public class PropertiesDialog : IDEDialog { public interface IMultiValued { void GetValue(int idx, String outValue); bool SetValue(int idx, StringView value); } class OwnedStringList : List { } protected class ValueContainer { public T mValue; } protected class MoveItemWidget : Widget { public int32 mArrowDir; public override void Draw(Graphics g) { base.Draw(g); //g.FillRect(0, 0, mWidth, mHeight); if (mArrowDir == -1) { using (g.PushScale(1, -1)) g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ArrowMoveDown), 0, -20); } else { g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ArrowMoveDown), 0, 0); } } } protected class PropPage { public enum Flags { None = 0, AllowSearch = 1, AllowReset = 2, } public int32 mCategoryType; public DarkListView mPropertiesListView ~ delete _; public Dictionary mPropEntries = new .() ~ delete _; public bool mHasChanges; public Flags mFlags; public this() { //Debug.WriteLine("PropPage this {0}", this); } public ~this() { //Debug.WriteLine("PropPage ~this {0}", this); for (var propEntryArr in mPropEntries.Values) { for (var propEntry in propEntryArr) delete propEntry; delete propEntryArr; } if (mPropertiesListView.mParent != null) mPropertiesListView.RemoveSelf(); } } protected class PropEntry { public enum Flags { None, BrowseForFile, BrowseForFolder, } public enum MultiEncodeKind { Semicolon, Extended // Uses /v to split } public Flags mFlags; public DarkListViewItem mListViewItem; public Object mTarget; public String mPropertyName ~ delete _; public FieldInfo mFieldInfo; public Action mApplyAction ~ delete _; public String[] mOptionValues ~ DeleteContainerAndItems!(_); public String mNotSetString; public List mComboBoxes ~ delete _; public DarkCheckBox mCheckBox; public Variant mCurValue ~ DisposeVariant(ref _); public Variant mOrigValue ~ DisposeVariant(ref _); public PropertyBag mProperties ~ delete _; public Event mOnUpdate ~ _.Dispose(); public bool mDisabled; public bool mMultiRootReadOnly; public uint32? mColorOverride; public String mRelPath ~ delete _; public bool mIsTypeWildcard; public bool mAllowMultiline; public bool mReadOnly; public Insets mEditInsets ~ delete _; public ~this() { } public static void DisposeVariant(ref Variant variant) { if ((variant.OwnsMemory) && (variant.IsObject)) { var obj = variant.Get(); if (var strList = obj as String[]) { for (var str in strList) delete str; } if (var strList = obj as List) { for (var str in strList) delete str; } if (var keyList = obj as List) { for (var keyState in keyList) delete keyState; } } variant.Dispose(); } public static bool IsVariantEqual(Variant lhs, Variant rhs) { //var curEq = lhs as IEquatable; //if (curEq.Equals(rhs)) //return false; //TODO: Implement equals var type = lhs.VariantType; if (type.IsObject) { Object obj = lhs.Get(); var iEquatable = obj as IEquatable; if (iEquatable != null) return iEquatable.Equals(rhs.Get()); } #unwarn var origType = rhs.VariantType; Debug.Assert(type == rhs.VariantType); #unwarn var keyStateListType = typeof(List); if (type == typeof(String)) return Variant.Equals!(lhs, rhs); else if (type == typeof(bool)) return Variant.Equals!(lhs, rhs); else if (type == typeof(Color)) return Variant.Equals!(lhs, rhs); else if (type.IsNullable) { return lhs == rhs; } else if (type == typeof(List)) { let curVal = lhs.Get>(); let origVal = rhs.Get>(); if (curVal.Count != origVal.Count) return false; for (int32 i = 0; i < curVal.Count; i++) { if (curVal[i] != origVal[i]) return false; } return true; } else if (lhs.VariantType.IsGenericType) { let curVal = lhs.Get>(); let origVal = rhs.Get>(); if (curVal.Count != origVal.Count) return false; for (int32 i = 0; i < curVal.Count; i++) { if (curVal[i] != origVal[i]) return false; } return true; } else if (type.IsObject) { var lhsObj = lhs.Get(); var rhsObj = rhs.Get(); if ((var lhsEq = lhsObj as IEquatable) && (var rhsEq = rhsObj as IEquatable)) { return lhsEq.Equals(rhsEq); } return false; } else // Could be an int or enum return Variant.Equals!(lhs, rhs); } public bool HasChanged() { return !IsVariantEqual(mCurValue, mOrigValue); } public void ApplyValue() { var type = mCurValue.VariantType; if (type == typeof(String)) { if (mApplyAction != null) mApplyAction(); String prevString = mOrigValue.Get(); String curString = mCurValue.Get(); prevString.Set(curString); } else if (type == typeof(List)) { let prevList = mOrigValue.Get>(); let curList = mCurValue.Get>(); ClearAndDeleteItems(prevList); for (var keyState in curList) { prevList.Add(keyState.Clone()); } } else if (type == typeof(List)) { List prevList = mOrigValue.Get>(); List curList = mCurValue.Get>(); ClearAndDeleteItems(prevList); for (var str in curList) { prevList.Add(new String(str)); } } else { if (mApplyAction != null) { mApplyAction(); } else { Debug.Assert(type.IsPrimitive || type.IsEnum || type.IsNullable); Debug.Assert(!mCurValue.OwnsMemory); // 'Large' structs not supported mOrigValue = mCurValue; } if (mFieldInfo != default(FieldInfo)) mFieldInfo.SetValue(mTarget, mCurValue); } } /*public void Init() { }*/ } protected const uint32 cHeaderColor = 0xFFE8E8E8; public class CategoryListViewItem : DarkListViewItem { public int32 mCategoryIdx; public override void DrawSelect(Graphics g) { using (g.PushColor(mListView.mHasFocus ? 0xFFFFFFFF : 0x80FFFFFF)) base.DrawSelect(g); } } public class CategoryListView : DarkListView { protected override ListViewItem CreateListViewItem() { return new CategoryListViewItem(); } public override void KeyDown(KeyCode keyCode, bool isRepeat) { var focusedListViewItem = GetRoot().FindFocusedItem(); bool changeFocus = (keyCode == .Tab) && (mWidgetWindow.GetKeyFlags(true) == .None); if ((keyCode == .Right) && ((focusedListViewItem == null) || (focusedListViewItem.GetChildCount() == 0))) changeFocus = true; if (changeFocus) { var propertiesDialog = (PropertiesDialog)mParent; var propListView = propertiesDialog.mPropPage.mPropertiesListView; propListView.SetFocus(); return; } base.KeyDown(keyCode, isRepeat); } } public class PropListViewItem : DarkListViewItem { public override void DrawSelect(Graphics g) { SelfToOtherTranslate(mListView, 0, 0, var parentX, var parentY); var width = mListView.mColumns[0].mWidth; using (g.PushColor(mListView.mHasFocus ? 0xFFFFFFFF : 0x80FFFFFF)) using (g.PushColor(0x20FFFFFF)) g.FillRect(-parentX, 0, width, mHeight); } } public class PropListView : DarkListView { protected override ListViewItem CreateListViewItem() { return new PropListViewItem(); } public override void SetFocus() { base.SetFocus(); if (GetRoot().FindFocusedItem() == null) { for (int idx < GetRoot().GetChildCount()) { var item = GetRoot().GetChildAtIndex(idx); if (item.mSelfHeight > 0) { item.Focused = true; break; } } } } public override void KeyDown(KeyCode keyCode, bool isRepeat) { var propertiesDialog = (PropertiesDialog)mParent; let keyFlags = mWidgetWindow.GetKeyFlags(true); bool changeFocus = (keyCode == .Tab) && (keyFlags == .Shift); if (keyCode == .Left) { var selectedItem = GetRoot().FindFocusedItem(); if ((selectedItem != null) && (selectedItem.mParentItem == GetRoot())) changeFocus = true; } if (changeFocus) { propertiesDialog.mCategorySelector.SetFocus(); return; } base.KeyDown(keyCode, isRepeat); } public override void MouseDown(float x, float y, int32 btn, int32 btnCount) { base.MouseDown(x, y, btn, btnCount); GetRoot().SelectItemExclusively(null); } } protected Object mCurPropertiesTarget; protected Object[] mCurPropertiesTargets ~ delete _; protected CategoryListView mCategorySelector; protected bool mCancelingEdit; protected PropPage mPropPage; protected EditWidget mPropEditWidget; protected ButtonWidget mApplyButton; protected SearchEditWidget mSearchEdit; protected DarkButton mResetButton; protected PropEntry[] mEditingProps ~ delete _; protected ListViewItem mEditingListViewItem; protected Widget mPreviousFocus; protected List mCategoryListViewItems = new List() ~ delete _; protected bool mHideSelector; protected virtual float TopY { get { return GS!(32); } } public this() { //mMinWidth = GS!(320); mWindowFlags = .ClientSized | .TopMost | .Caption | .Border | .SysMenu | .Resizable | .PopupPosition; mButtonBottomMargin = GS!(6); mButtonRightMargin = GS!(6); AddOkCancelButtons(new (evt) => { evt.mCloseDialog = ApplyChanges(); }, null, 0, 1); mApplyButton = AddButton("Apply", new (evt) => { evt.mCloseDialog = false; ApplyChanges(); }); mCategorySelector = new CategoryListView(); mCategorySelector.mHiliteOffset = GS!(-2); mCategorySelector.SetShowHeader(false); mCategorySelector.InitScrollbars(false, false); mCategorySelector.mAutoFocus = true; mCategorySelector.mAllowMultiSelect = false; mCategorySelector.mOnKeyDown.Add(new (key) => ChildKeyDown(key.mKeyCode)); mCategorySelector.mOnFocusChanged.Add(new (listViewItem) => { if (listViewItem.Focused) CategoryChanged(listViewItem); }); mCategorySelector.mChildIndent = GS!(16); AddWidget(mCategorySelector); mTabWidgets.Insert(0, mCategorySelector); } public ~this() { } public override void AddedToParent() { base.AddedToParent(); mCategorySelector.SetFocus(); } protected override void RehupMinSize() { mWidgetWindow.SetMinimumSize(GS!(320), GS!(180), true); } public override bool HandleTab(int dir) { if ((dir == 1) && (var propListView = mWidgetWindow.mFocusWidget as PropListView)) { var selectedItem = propListView.GetRoot().FindFocusedItem(); if ((selectedItem != null) && (selectedItem.GetSubItemCount() == 2)) { MouseEvent evt = scope .(); var valueItem = selectedItem.GetSubItem(1); evt.mX = -1; evt.mY = -1; evt.mSender = valueItem; valueItem.mOnMouseDown(evt); return true; } } return base.HandleTab(dir); } protected void CreatePropPage(int32 categoryType, PropPage.Flags flags = .None) { mPropPage = new PropPage(); mPropPage.mCategoryType = categoryType; mPropPage.mFlags = flags; mPropPage.mPropertiesListView = new PropListView(); mPropPage.mPropertiesListView.mAutoFocus = false; mPropPage.mPropertiesListView.mAllowMultiSelect = false; mPropPage.mPropertiesListView.mHiliteOffset = GS!(-2); var column = mPropPage.mPropertiesListView.AddColumn(GS!(220), "Title"); column.mMinWidth = GS!(80); mPropPage.mPropertiesListView.AddColumn(GS!(180), "Value"); } protected void RemovePropPage() { if (mPropPage != null) { mTabWidgets.Remove(mPropPage.mPropertiesListView); mPropPage.mPropertiesListView.RemoveSelf(); mPropPage = null; } } protected virtual void ShowPropPage(int32 categoryTypeInt) { RemovePropPage(); MarkDirty(); } protected void UpdateSearch() { String searchStr = scope String(); if (mSearchEdit != null) { mSearchEdit.GetText(searchStr); searchStr.Trim(); } var listView = mPropPage.mPropertiesListView; float lineSpacing = listView.mFont.GetLineSpacing(); if (searchStr.IsEmpty) { listView.GetRoot().WithItems(scope (item) => { item.mSelfHeight = lineSpacing; }); } else { listView.GetRoot().WithItems(scope (listViewItem) => { listViewItem.mSelfHeight = 0; }); listView.GetRoot().WithItems(scope (listViewItem) => { if (listViewItem.GetSubItemCount() < 2) return; bool found = false; if (listViewItem.Label.IndexOf(searchStr, true) != -1) found = true; for (int32 subItemIdx = 1; subItemIdx < listViewItem.GetSubItemCount(); subItemIdx++) { var valueItem = listViewItem.GetSubItem(subItemIdx); if (valueItem.Label.IndexOf(searchStr, true) != -1) found = true; } if (found) { var checkItem = listViewItem; while (checkItem != listView.GetRoot()) { checkItem.mSelfHeight = lineSpacing; checkItem = checkItem.mParentItem; } } else listViewItem.Selected = false; }); } listView.Resized(); } public void AddPropPageWidget() { int defaultButtonIdx = mTabWidgets.IndexOf(mDefaultButton); mTabWidgets.Insert(defaultButtonIdx, mPropPage.mPropertiesListView); AddWidget(mPropPage.mPropertiesListView); UpdateSearch(); } protected void CategoryChanged(ListViewItem listViewItem) { //String category = listViewItem.mLabel; //int32 categoryType = listViewItem.mParentItem.GetIndexOfChild(listViewItem); ShowPropPage(((CategoryListViewItem)listViewItem).mCategoryIdx); } public bool AssertNotCompilingOrRunning() { var app = IDEApp.sApp; if (app.IsCompiling) { app.Fail("Changes cannot be applied while compiling."); return false; } if (app.mDebugger.mIsRunning) { app.Fail("Changes cannot be applied while debugging."); return false; } return true; } protected void ApplyChanges(PropPage propPage, ref bool hadChanges) { for (var propEntries in propPage.mPropEntries.Values) { for (var propEntry in propEntries) { if (propEntry.HasChanged()) { hadChanges = true; propEntry.ApplyValue(); } } if (propPage == mPropPage) UpdatePropertyValue(propEntries); } propPage.mHasChanges = false; } protected virtual bool ApplyChanges() { return true; } protected void ChildKeyDown(KeyCode keyCode) { if (keyCode == KeyCode.Escape) { KeyDown(keyCode, false); } else if (keyCode == KeyCode.Return) { mDefaultButton.SetFocus(); mDefaultButton.KeyDown(keyCode, false); } } protected virtual void ResetSettings() { } public override void ResizeComponents() { base.ResizeComponents(); float topY = TopY; float propTopY = topY; //var font = DarkTheme.sDarkTheme.mSmallFont; bool hasTop = false; float rightX = mWidth - GS!(12); float selectorWidth = GS!(140); if (mHideSelector) selectorWidth = 0; // Reset Settings if (mPropPage.mFlags.HasFlag(.AllowReset)) { if (mResetButton == null) { mResetButton = new DarkButton(); mResetButton.Label = "Reset Settings"; mResetButton.mOnMouseClick.Add(new (dlg) => { ResetSettings(); }); AddWidget(mResetButton); mTabWidgets.Insert(1, mResetButton); } float btnWidth = DarkTheme.sDarkTheme.mSmallFont.GetWidth(mResetButton.Label) + GS!(24); mResetButton.Resize(mWidth - btnWidth - GS!(6), topY, btnWidth, GS!(22)); hasTop = true; rightX = Math.Max(mResetButton.mX - GS!(6), 0); } else if (mResetButton != null) { mTabWidgets.Remove(mResetButton); mResetButton.RemoveSelf(); DeleteAndNullify!(mResetButton); } // Search edit if (mPropPage.mFlags.HasFlag(.AllowSearch)) { if (mSearchEdit == null) { mSearchEdit = new SearchEditWidget(); AddWidget(mSearchEdit); mTabWidgets.Insert(1, mSearchEdit); } float x = selectorWidth + GS!(12); if (selectorWidth == 0) x = GS!(6); mSearchEdit.Resize(x, topY, Math.Max(rightX - x, 0), GS!(22)); hasTop = true; } else if (mSearchEdit != null) { mTabWidgets.Remove(mSearchEdit); mSearchEdit.RemoveSelf(); DeleteAndNullify!(mSearchEdit); UpdateSearch(); } if (hasTop) { propTopY += GS!(26); } float catRight; if (selectorWidth > 0) { catRight = mCategorySelector.mX + mCategorySelector.mWidth; mCategorySelector.SetVisible(true); mCategorySelector.Resize(GS!(6), topY, selectorWidth, Math.Max(mHeight - topY - GS!(32), 0)); } else { catRight = 0; mCategorySelector.SetVisible(false); } mPropPage.mPropertiesListView.Resize(catRight + GS!(6), propTopY, Math.Max(mWidth - catRight - GS!(12), 0), Math.Max(mHeight - propTopY - GS!(32), 0)); if (mPropEditWidget != null) { DarkListViewItem editItem = (DarkListViewItem)mEditingListViewItem; let propEntry = mEditingProps[0]; float xPos; float yPos; editItem.SelfToOtherTranslate(editItem.mListView, 0, -1, out xPos, out yPos); Rect rect = .(xPos, yPos, GetValueEditWidth(editItem), GS!(20)); propEntry.mEditInsets?.ApplyTo(ref rect); if (mPropEditWidget.mEditWidgetContent.mIsMultiline) rect.mHeight *= 4; mPropEditWidget.Resize(rect.Left, rect.Top, rect.Width, rect.mHeight); //mPropEditWidget.Resize(mPropEditWidget.mX, mPropEditWidget.mY, GetValueEditWidth(mEditingListViewItem.GetSubItem(1)), mPropEditWidget.mHeight); if (mPropEditWidget.mChildWidgets != null) { for (var childWidget in mPropEditWidget.mChildWidgets) childWidget.mX = mPropEditWidget.mWidth - childWidget.mWidth; } } } void CategoryValueClicked(MouseEvent theEvent) { DarkListViewItem clickedItem = (DarkListViewItem)theEvent.mSender; mCategorySelector.GetRoot().SelectItemExclusively(clickedItem); mCategorySelector.SetFocus(); } protected DarkListViewItem AddCategoryItem(DarkListViewItem parent, String name) { var item = (CategoryListViewItem)parent.CreateChildItem(); item.Label = name; item.mFocusColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFA0A0A0); item.mOnMouseDown.Add(new => CategoryValueClicked); item.mCategoryIdx = (int32)mCategoryListViewItems.Count; mCategoryListViewItems.Add(item); return item; } protected void HandleEditLostFocus(Widget widget) { Debug.Assert(widget == mPropEditWidget); EditWidget editWidget = (EditWidget)widget; editWidget.mOnLostFocus.Remove(scope => HandleEditLostFocus, true); editWidget.mOnSubmit.Remove(scope => HandleSubmit, true); editWidget.mOnCancel.Remove(scope => HandleCancel, true); mWidgetWindow.mOnMouseWheel.Remove(scope => HandleMouseWheel, true); mWidgetWindow.mOnWindowMoved.Remove(scope => HandleWindowMoved, true); String newValue = scope String(); editWidget.GetText(newValue); newValue.Trim(); DarkListViewItem rootItem = (DarkListViewItem)mEditingListViewItem.GetSubItem(0); //DarkListViewItem valueItem = (DarkListViewItem)item.GetSubItem(1); if (!editWidget.mEditWidgetContent.HasUndoData()) mCancelingEdit = true; if (!mCancelingEdit) { var editingProp = mEditingProps[0]; bool multiCopyToOthers = false; bool handled = false; var curVariantType = editingProp.mCurValue.VariantType; if (curVariantType.IsNullable) { let elementType = ((SpecializedGenericType)curVariantType).GetGenericArg(0); if (elementType.[Friend]mTypeCode == .Int32) { handled = true; editingProp.mCurValue.[Friend]mData = 0; int32? value = null; if ((newValue.Length != 0) && (!String.Equals(newValue, "Not Set", .OrdinalIgnoreCase))) { if (int32.Parse(newValue) case .Ok(let intVal)) { value = intVal; } else { let error = scope String(); error.AppendF("Invalid number: {0}", newValue); gApp.Fail(error); } } *((bool*)&editingProp.mCurValue.[Friend]mData + elementType.[Friend]mSize) = value.HasValue; if (value.HasValue) { void* valueData = editingProp.mCurValue.GetValueData(); Internal.MemCpy(valueData, &value.[Friend]mValue, elementType.[Friend]mSize); } multiCopyToOthers = true; } } if (handled) { // } else if (editingProp.mListViewItem != rootItem) { List curEntries = editingProp.mCurValue.Get>(); List entries = new List(curEntries.GetEnumerator()); for (int32 childIdx = 0; childIdx < editingProp.mListViewItem.GetChildCount(); childIdx++) { if (rootItem == editingProp.mListViewItem.GetChildAtIndex(childIdx)) { if (childIdx >= entries.Count) entries.Add(new String(newValue)); else entries[childIdx].Set(newValue); } } curEntries.Clear(); for (int32 i = 0; i < entries.Count; i++) { entries[i].Trim(); if (entries[i] == "") { delete entries[i]; entries.RemoveAt(i); i--; } } PropEntry.DisposeVariant(ref editingProp.mCurValue); editingProp.mCurValue = Variant.Create(entries, true); multiCopyToOthers = true; } else { var prevValue = editingProp.mCurValue; bool setValue = true; if (curVariantType == typeof(List)) { if (!newValue.StartsWith("<")) { let entries = new List(); if (!newValue.IsEmpty) KeyState.Parse(newValue, entries); editingProp.mCurValue = Variant.Create(entries, true); } else editingProp.mCurValue = DuplicateVariant(editingProp.mOrigValue); } else if (curVariantType == typeof(List)) { let entryViews = scope List(); entryViews.AddRange(newValue.Split(';')); let entries = new List(); for (int32 i = 0; i < entryViews.Count; i++) { String entry = scope String(entryViews[i]); entry.Trim(); if (entry.Length > 0) { entries.Add(new String(entry)); } } editingProp.mCurValue = Variant.Create(entries, true); } else if (curVariantType.[Friend]mTypeCode == .Int32) { if (int32.Parse(newValue) case .Ok(let intVal)) { editingProp.mCurValue = Variant.Create(intVal); } else { let error = scope String(); error.AppendF("Invalid number: {0}", newValue); gApp.Fail(error); } } else if (curVariantType == typeof(Color)) { if (int64.Parse(newValue, .HexNumber) case .Ok(let intVal)) { editingProp.mCurValue = Variant.Create((Color)((uint32)intVal | 0xFF000000)); } else { let error = scope String(); error.AppendF("Invalid number: {0}", newValue); gApp.Fail(error); setValue = false; } } else if (curVariantType.[Friend]mTypeCode == .Float) { if (float.Parse(newValue) case .Ok(let floatVal)) { editingProp.mCurValue = Variant.Create(floatVal); } else { let error = scope String(); error.AppendF("Invalid number: {0}", newValue); gApp.Fail(error); setValue = false; } } else if ((curVariantType.IsObject) && (var multiValue = prevValue.Get() as IMultiValued)) { multiValue.SetValue(mEditingListViewItem.mColumnIdx - 1, newValue); setValue = false; } else editingProp.mCurValue = Variant.Create(new String(newValue), true); if (setValue) { PropEntry.DisposeVariant(ref prevValue); multiCopyToOthers = true; } } if (multiCopyToOthers) { for (int propIdx = 1; propIdx < mEditingProps.Count; propIdx++) { var otherProp = mEditingProps[propIdx]; PropEntry.DisposeVariant(ref otherProp.mCurValue); otherProp.mCurValue = DuplicateVariant(editingProp.mCurValue); } } UpdatePropertyValue(mEditingProps); } editWidget.RemoveSelf(); gApp.DeferDelete(editWidget); mPropEditWidget = null; DeleteAndNullify!(mEditingProps); mEditingListViewItem = null; if ((mPreviousFocus != null) && (mPreviousFocus.mWidgetWindow != null)) mPreviousFocus.SetFocus(); mPreviousFocus = null; if (mWidgetWindow.mFocusWidget == null) mDefaultButton.SetFocus(); CheckForChanges(); } protected virtual bool HasChanges() { return false; } protected void CheckForChanges() { bool hasChanges = HasChanges(); for (var propEntries in mPropPage.mPropEntries.Values) { for (var propEntry in propEntries) if (propEntry.HasChanged()) hasChanges = true; var propEntry = propEntries[0]; if (propEntry.mComboBoxes != null) { for (var comboBox in propEntry.mComboBoxes) { if (comboBox.mCurMenuWidget != null) comboBox.mBkgColor = 0x20FFFFFF; else comboBox.mBkgColor = 0; } } } mPropPage.mHasChanges = hasChanges; } protected void CheckForChanges(PropPage[] propPages) { CheckForChanges(); bool hasChanges = false; for (int32 pageIdx = 0; pageIdx < propPages.Count; pageIdx++) { var propPage = propPages[pageIdx]; bool isCurrent = propPage == mPropPage; if (propPage != null) { var categoryItem = mCategoryListViewItems[pageIdx]; if (isCurrent) categoryItem.mIsBold = propPage.mHasChanges; hasChanges |= propPage.mHasChanges; } } if (hasChanges) mApplyButton.mDisabled = false; else mApplyButton.mDisabled = true; } protected void ChangeProperty(PropEntry[] propEntries, Variant value) { for (var propEntry in propEntries) { let curVariantType = propEntry.mCurValue.VariantType; if (propEntry.mCurValue.VariantType.IsNullable) { let genericType = (SpecializedGenericType)curVariantType; let elementType = genericType.GetGenericArg(0); propEntry.mCurValue.[Friend]mData = 0; *((bool*)&propEntry.mCurValue.[Friend]mData + elementType.[Friend]mSize) = value.HasValue; if (value.HasValue) { void* valueData = propEntry.mCurValue.GetValueData(); var copyValue = value; Internal.MemCpy(valueData, copyValue.GetValueData(), elementType.[Friend]mSize); } } else { PropEntry.DisposeVariant(ref propEntry.mCurValue); propEntry.mCurValue = value; } } UpdatePropertyValue(propEntries); } void HandleCancel(EditEvent theEvent) { mCancelingEdit = true; HandleEditLostFocus((EditWidget)theEvent.mSender); } protected void HandleSubmit(EditEvent theEvent) { if ((mPropEditWidget != null) && (var sewc = mPropEditWidget.mEditWidgetContent as SourceEditWidgetContent)) { if (sewc.mAutoComplete != null) return; } HandleEditLostFocus((EditWidget)theEvent.mSender); } protected void MoveEditingItem(int32 subValueIdx, int32 dir) { ListViewItem item = mEditingListViewItem; var itemParent = item.mParentItem; String editText = scope String(); mPropEditWidget.GetText(editText); PropEntry propEntry = mEditingProps[0]; var propEntries = new PropEntry[mEditingProps.Count]; defer delete propEntries; mEditingProps.CopyTo(propEntries); var curStringList = propEntry.mCurValue.Get>(); List stringList = new List(); for (var str in curStringList) { stringList.Add(new String(str)); } mCancelingEdit = true; HandleEditLostFocus(mPropEditWidget); if (subValueIdx >= stringList.Count) stringList.Add(editText); String swap = stringList[subValueIdx]; stringList[subValueIdx] = stringList[subValueIdx + dir]; stringList[subValueIdx + dir] = swap; PropEntry.DisposeVariant(ref propEntry.mCurValue); propEntry.mCurValue = Variant.Create(stringList, true); for (int otherIdx = 1; otherIdx < propEntries.Count; otherIdx++) { var otherProps = propEntries[otherIdx]; PropEntry.DisposeVariant(ref otherProps.mCurValue); otherProps.mCurValue = DuplicateVariant(propEntry.mCurValue); } UpdatePropertyValue(propEntries); ListViewItem newItem = itemParent.GetChildAtIndex(subValueIdx + dir); EditValue(newItem, propEntries, subValueIdx + dir); mPropEditWidget.SetText(editText); } protected void EditValue(ListViewItem item, PropEntry[] propEntries, int32 subValueIdx = -1) { DarkListViewItem editItem = (DarkListViewItem)item.GetSubItem(1); mPreviousFocus = null; if ((mWidgetWindow.mFocusWidget == mPropPage.mPropertiesListView) && (mPropPage.mPropertiesListView.GetRoot().FindFocusedItem() != null)) mPreviousFocus = mWidgetWindow.mFocusWidget; mCancelingEdit = false; var propEntry = propEntries[0]; if ((propEntry.mMultiRootReadOnly) && (subValueIdx == -1)) return; var valueItem = propEntry.mListViewItem.GetSubItem(1); let type = propEntry.mCurValue.VariantType; DarkEditWidget editWidget; if (propEntry.mIsTypeWildcard) editWidget = new TypeWildcardEditWidget(); else if (propEntry.mRelPath != null) { var pathEditWidget = new PathEditWidget(); pathEditWidget.mRelPath = new String(propEntry.mRelPath); editWidget = pathEditWidget; } else if (type == typeof(List)) { editWidget = new KeysEditWidget(); editWidget.Content.mIsReadOnly = true; } else editWidget = new DarkEditWidget(); let ewc = editWidget.mEditWidgetContent; ewc.mIsMultiline = propEntry.mAllowMultiline; if (ewc.mIsMultiline) editWidget.InitScrollbars(false, true); if (propEntry.mReadOnly) editWidget.mEditWidgetContent.mIsReadOnly = true; editWidget.mScrollContentInsets.Set(GS!(3), GS!(3), GS!(1), GS!(3)); editWidget.Content.mTextInsets.Set(GS!(-3), GS!(2), 0, GS!(2)); //editWidget.RehupSize(); if (subValueIdx != -1) { var obj = propEntry.mCurValue.Get(); if (var multiValued = obj as IMultiValued) { var label = multiValued.GetValue(subValueIdx, .. scope .()); editWidget.SetText(label); } else { List stringList = obj as List; if (subValueIdx < stringList.Count) editWidget.SetText(stringList[subValueIdx]); MoveItemWidget moveItemWidget; if (subValueIdx > 0) { moveItemWidget = new MoveItemWidget(); editWidget.AddWidget(moveItemWidget); moveItemWidget.Resize(6, editWidget.mY - GS!(16), GS!(20), GS!(20)); moveItemWidget.mArrowDir = -1; moveItemWidget.mOnMouseDown.Add(new (evt) => { MoveEditingItem(subValueIdx, -1); }); if (!ewc.mIsMultiline) editWidget.mOnKeyDown.Add(new (evt) => { if (evt.mKeyCode == KeyCode.Up) MoveEditingItem(subValueIdx, -1); }); } if (subValueIdx < stringList.Count - 1) { moveItemWidget = new MoveItemWidget(); editWidget.AddWidget(moveItemWidget); moveItemWidget.Resize(6, editWidget.mY + GS!(16), GS!(20), GS!(20)); moveItemWidget.mArrowDir = 1; moveItemWidget.mOnMouseDown.Add(new (evt) => { MoveEditingItem(subValueIdx, 1); }); if (!ewc.mIsMultiline) editWidget.mOnKeyDown.Add(new (evt) => { if (evt.mKeyCode == KeyCode.Down) MoveEditingItem(subValueIdx, 1); }); } } } else { String label = valueItem.mLabel; if ((propEntry.mNotSetString != null) && (label == propEntry.mNotSetString)) label = ""; /*var curVariantType = mEditingProp.mCurValue.VariantType; if (curVariantType.IsNullable) { if (label == "Not Set") label = ""; }*/ if (propEntry.mCurValue.VariantType == typeof(List)) { List stringList = propEntry.mCurValue.Get>(); String allValues = scope .(); for (int i = 0; i < stringList.Count; i++) { if (i > 0) allValues.Append(";"); allValues.Append(stringList[i]); } editWidget.SetText(allValues); } else if (editWidget is KeysEditWidget) editWidget.SetText("< Press Key >"); else editWidget.SetText(label); } editWidget.Content.SelectAll(); editItem.mListView.AddWidget(editWidget); editWidget.mOnLostFocus.Add(new => HandleEditLostFocus); editWidget.mOnSubmit.Add(new => HandleSubmit); editWidget.mOnCancel.Add(new => HandleCancel); editWidget.SetFocus(); mWidgetWindow.mOnMouseWheel.Add(new => HandleMouseWheel); mWidgetWindow.mOnWindowMoved.Add(new => HandleWindowMoved); editWidget.mEditWidgetContent.ClearUndoData(); mEditingListViewItem = item; mPropEditWidget = editWidget; mEditingProps = new PropEntry[propEntries.Count]; for (int i < propEntries.Count) mEditingProps[i] = propEntries[i]; ResizeComponents(); } void HandleMouseWheel(MouseEvent evt) { mCancelingEdit = true; HandleEditLostFocus(mPropEditWidget); } void HandleWindowMoved(BFWindow window) { mCancelingEdit = true; HandleEditLostFocus(mPropEditWidget); } protected virtual void UpdatePropertyValue(PropEntry[] propEntries) { var propEntry = propEntries[0]; if (propEntry.mListViewItem == null) return; bool areDifferent = false; for (int checkIdx = 1; checkIdx < propEntries.Count; checkIdx++) { var checkProp = propEntries[checkIdx]; if (!PropEntry.IsVariantEqual(checkProp.mCurValue, propEntry.mCurValue)) areDifferent = true; } bool hasChanged = false; for (var checkPropEntry in propEntries) { if (checkPropEntry.HasChanged()) hasChanged = true; } /*if (propEntry.mFieldInfo == default(FieldInfo)) return;*/ var curVariantType = propEntry.mCurValue.VariantType; bool handled = false; if (propEntry.mOnUpdate.HasListeners) { handled |= propEntry.mOnUpdate(); } bool isNotSet = false; var valueItem = (DarkListViewItem)propEntry.mListViewItem.GetSubItem(1); if (curVariantType.IsNullable) { propEntry.mNotSetString = "Not Set"; let genericType = (SpecializedGenericType)curVariantType; let elementType = genericType.GetGenericArg(0); bool hasValue = *((bool*)&propEntry.mCurValue.[Friend]mData + elementType.[Friend]mSize); curVariantType = elementType; if (!hasValue) { valueItem.Label = "Not Set"; handled = true; isNotSet = true; } } void FixLabel(ListViewItem lvItem) { if (!lvItem.mLabel.Contains('\n')) return; lvItem.mLabel.Replace('\n', ' '); } if (handled) { if (curVariantType == typeof(String)) { var str = propEntry.mCurValue.Get(); if ((propEntry.mNotSetString != null) && ((propEntry.mNotSetString == str) || (str.IsEmpty))) { isNotSet = true; str = propEntry.mNotSetString; } valueItem.Label = str; } } else if (propEntry.mOptionValues != null) { int32 idx = 0; if (curVariantType == typeof(bool)) { if (propEntry.mCurValue.Get()) idx = 1; } else if (curVariantType.IsEnum) { //idx = propEntry.mCurValue.Get(); int64 val = 0; Internal.MemCpy(&val, propEntry.mCurValue.GetValueData(), curVariantType.Size); idx = (int32)val; } valueItem.Label = propEntry.mOptionValues[idx]; } else if (curVariantType == typeof(List)) { var keyList = propEntry.mCurValue.Get>(); var str = scope String(); KeyState.ToString(keyList, str); if (str.IsEmpty) { let origEntry = propEntry.mOrigValue.Get>(); if (!origEntry.IsEmpty) str.Append("< Removed >"); } valueItem.Label = str; } else if (curVariantType.IsGenericType) { String allValues = scope String(); List strVals; if (areDifferent) strVals = scope:: .(); else strVals = propEntry.mCurValue.Get>(); for (int32 i = 0; i <= strVals.Count; i++) { String curValue = ""; if (i < strVals.Count) { if (i > 0) allValues.Append(";"); curValue = strVals[i]; allValues.Append(curValue); if (curValue.Contains(';')) // Don't allow editing if the parsing will mess up propEntry.mMultiRootReadOnly = true; } DarkListViewItem childItem; DarkListViewItem childSubItem; if (i >= propEntry.mListViewItem.GetChildCount()) { childItem = (DarkListViewItem)propEntry.mListViewItem.CreateChildItem(); childSubItem = (DarkListViewItem)childItem.CreateSubItem(1); int32 curIdx = i; PropEntry[] propEntriesCopy = new PropEntry[propEntries.Count]; propEntries.CopyTo(propEntriesCopy); childSubItem.mOnMouseDown.Add(new (evt) => { if (areDifferent) { var strList = propEntry.mCurValue.Get>(); ClearAndDeleteItems(strList); } ArrayPropValueClicked(propEntriesCopy, curIdx); } ~ { delete propEntriesCopy; }); } else { childItem = (DarkListViewItem)propEntry.mListViewItem.GetChildAtIndex(i); childSubItem = (DarkListViewItem)childItem.GetSubItem(1); } if (i < strVals.Count) { childItem.Label = scope String()..AppendF("#{0}", i + 1); childSubItem.mTextColor = DarkTheme.COLOR_TEXT; } else { childItem.Label = ""; childSubItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFC0C0C0); } childSubItem.Label = curValue; FixLabel(childSubItem); } while (propEntry.mListViewItem.GetChildCount() > strVals.Count + 1) propEntry.mListViewItem.RemoveChildItem(propEntry.mListViewItem.GetChildAtIndex(propEntry.mListViewItem.GetChildCount() - 1)); valueItem.Label = allValues; FixLabel(valueItem); } else if ((curVariantType.IsObject) && (var multiValue = propEntry.mCurValue.Get() as IMultiValued)) { for (int columnIdx in 1..(); } else if (curVariantType.IsEnum) { var enumStr = scope String(); Debug.Assert(curVariantType.Size <= 8); int64 val = 0; Internal.MemCpy(&val, propEntry.mCurValue.GetValueData(), curVariantType.Size); Enum.EnumToString(curVariantType, enumStr, val); GetEnumDisp(enumStr); valueItem.Label = enumStr; } else if (curVariantType == typeof(String)) { valueItem.Label = propEntry.mCurValue.Get(); } else if (curVariantType == typeof(int32)) { let valStr = scope String(); int32 intVal = propEntry.mCurValue.Get(); intVal.ToString(valStr); valueItem.Label = valStr; } else if (curVariantType == typeof(Color)) { let valStr = scope String(); int32 intVal = propEntry.mCurValue.Get(); (intVal & 0x00FFFFFF).ToString(valStr, "X6", null); valueItem.Label = valStr; } else if (curVariantType == typeof(float)) { let valStr = scope String(); float floatVal = propEntry.mCurValue.Get(); floatVal.ToString(valStr); valueItem.Label = valStr; } else if (curVariantType == typeof(Workspace.ConfigSelection)) { var platformItem = (DarkListViewItem)propEntry.mListViewItem.GetSubItem(2); var configSelection = propEntry.mCurValue.Get(); valueItem.Label = configSelection.mConfig; platformItem.Label = configSelection.mPlatform; } else ThrowUnimplemented(); //valueItem.Label = ToStackString!(propEntry.mCurValue); if (hasChanged) valueItem.mIsBold = true; else valueItem.mIsBold = false; if (areDifferent) { valueItem.Label = ""; valueItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFC0C0C0); } else if (propEntry.mColorOverride.HasValue) valueItem.mTextColor = propEntry.mColorOverride.Value; else if (isNotSet) valueItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFC0C0C0); else valueItem.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFFFFFFF); } void GetEnumDisp(String enumDisp) { int32 firstUpperChar = -1; for (int32 i = 0; i < enumDisp.Length; i++) { char8 c = enumDisp[i]; if (c.IsUpper) { if (firstUpperChar == -1) firstUpperChar = i; } if (c.IsLower) { if (firstUpperChar > 0) { enumDisp.Insert(firstUpperChar, " "); i++; } firstUpperChar = -1; } } if (firstUpperChar > 0) { enumDisp.Insert(firstUpperChar, " "); } } protected void PopulateComboBox(Menu menu, PropEntry[] propEntries) { var propEntry = propEntries[0]; var valueType = propEntry.mCurValue.VariantType; if (valueType.IsNullable) { let genericType = (SpecializedGenericType)valueType; let elementType = genericType.GetGenericArg(0); var menuItem = menu.AddItem("Not Set"); Variant newVariant = Variant(); menuItem.mOnMenuItemSelected.Add(new (evt) => { ChangeProperty(propEntries, newVariant); }); valueType = elementType; } if (propEntry.mOptionValues != null) { for (int32 i = 0; i < propEntry.mOptionValues.Count; i++) { var menuItem = menu.AddItem(propEntry.mOptionValues[i]); Variant newVariant = Variant(); if (valueType == typeof(bool)) newVariant = Variant.Create(i != 0); else if (valueType.IsEnum) { newVariant = Variant.Create(valueType, &i); } //newVariant = Variant.Create(!propEntry.mCurValue.Get()); menuItem.mOnMenuItemSelected.Add(new (evt) => { ChangeProperty(propEntries, newVariant); }); } } else { for (var enumValue in ((TypeInstance)valueType).GetFields()) { var enumStr = scope String(); enumStr.Append(enumValue.Name); GetEnumDisp(enumStr); var menuItem = menu.AddItem(enumStr); Variant newVariant = enumValue.GetValue(null); menuItem.mOnMenuItemSelected.Add(new (evt) => { ChangeProperty(propEntries, newVariant); }); } } } protected float GetValueEditWidth(ListViewItem subItem) { float width = subItem.mWidth; float transX; float transY; subItem.SelfToOtherTranslate(subItem.mListView, 0, 0, out transX, out transY); width = Math.Max(subItem.mListView.mWidth - transX - GS!(18), GS!(80)); return width; } static Variant DuplicateVariant(Variant prevVariant) { if (!prevVariant.HasValue) return Variant(); var type = prevVariant.VariantType; if (prevVariant.IsObject) { if (type == typeof(List)) { var newStrList = new List(); for (var str in prevVariant.Get>()) newStrList.Add(new String(str)); return Variant.Create(newStrList, true); } if (type == typeof(List)) { var newList = new List(); for (var keyState in prevVariant.Get>()) newList.Add(keyState.Clone()); return Variant.Create(newList, true); } if (type == typeof(String)) { var str = new String(prevVariant.Get()); return Variant.Create(str, true); } } else { //Variant.Create(); var data = prevVariant.[Friend]mData; return Variant.Create(prevVariant.VariantType, &data); /*if (type == typeof(bool)) return Variant.Create(prevVariant.Get()); if (type == typeof(int32)) return Variant.Create(prevVariant.Get()); if (type.IsEnum) return Variant.Create(prevVariant.VariantType, prevVariant.Get());*/ } ThrowUnimplemented(); } protected void UpdateFromTarget(Dictionary targetDict) { for (let propKV in mPropPage.mPropEntries) { #unwarn var listViewItem = propKV.key; var propEntries = propKV.value; for (var propEntry in propEntries) { Object target; if (!targetDict.TryGetValue(propEntry.mTarget, out target)) { //Debug.FatalError(); continue; } String usePropName = scope:: String(propEntry.mPropertyName); while (true) { int dotIdx = usePropName.IndexOf('.'); if (dotIdx == -1) break; String newTargetName = scope String(usePropName, 0, (int)dotIdx); usePropName.Remove(0, dotIdx + 1); int idx = -1; if (newTargetName.EndsWith("]")) { int bracketPos = newTargetName.IndexOf("["); String idxStr = scope String(newTargetName, bracketPos + 1, (int)newTargetName.Length - bracketPos - 2); idx = int32.Parse(idxStr).GetValueOrDefault(); newTargetName.RemoveToEnd(bracketPos); } Debug.Assert(target != null); var targetFieldInfo = target.GetType().GetField(newTargetName); targetFieldInfo.Value.GetValue(target, out target); if (idx != -1) { if (var iList = target as IList) { var vari = iList[idx]; Debug.Assert(vari.IsObject); target = vari.Get(); } } Debug.Assert(target != null); } var fieldInfo = target.GetType().GetField(usePropName).Value; var curValue = fieldInfo.GetValue(target).GetValueOrDefault(); PropEntry.DisposeVariant(ref propEntry.mCurValue); propEntry.mCurValue = DuplicateVariant(curValue); } UpdatePropertyValue(propEntries); } } void BrowseForFile(String outPath) { #if !CLI var fileDialog = scope OpenFileDialog(); fileDialog.Title = "Select File"; fileDialog.SetFilter("All files (*.*)|*.*"); if (!outPath.IsWhiteSpace) { String initialDir = scope .(); Path.GetDirectoryPath(outPath, initialDir); fileDialog.InitialDirectory = initialDir; } if (fileDialog.ShowDialog(mWidgetWindow).GetValueOrDefault() == .OK) { for (String openFileName in fileDialog.FileNames) { outPath.Set(openFileName); return; } } #endif } void BrowseForFolder(String outPath) { #if !CLI var folderDialog = scope FolderBrowserDialog(); if (!outPath.IsWhiteSpace) { folderDialog.SelectedPath = outPath; } if (folderDialog.ShowDialog(mWidgetWindow).GetValueOrDefault() == .OK) { outPath.Set(folderDialog.SelectedPath); } #endif } protected PropEntry SetupPropertiesItem(DarkListViewItem item, String name, Object propName = null, String[] optionValues = null, PropEntry.Flags flags = .None) { if ((propName == null) || (propName == "")) return null; DarkListViewItem subItem = null; if (item.GetSubItemCount() >= 2) subItem = (DarkListViewItem)item.GetSubItem(1); else subItem = (DarkListViewItem)item.CreateSubItem(1); if (mCurPropertiesTargets == null) { mCurPropertiesTargets = scope:: Object[1]; mCurPropertiesTargets[0] = mCurPropertiesTarget; defer:: { mCurPropertiesTargets = null; } } // //var comboBox = new DarkComboBox(); //Action act = new () => { comboBox.Resize(0, 0, GetValueEditWidth(subItem), subItem.mHeight + 1); }; // PropEntry[] propEntries = new PropEntry[mCurPropertiesTargets.Count]; for (int propIdx < (int)propEntries.Count) PropBlock: { Object target = mCurPropertiesTargets[propIdx]; String usePropName = propName as String; if (usePropName != null) { usePropName = scope:PropBlock String(usePropName); } else if (var propNames = propName as String[]) { usePropName = scope:PropBlock String(propNames[propIdx]); } String[] useOptionValues = optionValues; while (true) { int dotIdx = usePropName.IndexOf('.'); if (dotIdx == -1) break; String newTargetName = scope String(usePropName, 0, (int)dotIdx); usePropName.Remove(0, dotIdx + 1); int idx = -1; if (newTargetName.EndsWith("]")) { int bracketPos = newTargetName.IndexOf("["); String idxStr = scope String(newTargetName, bracketPos + 1, (int)newTargetName.Length - bracketPos - 2); idx = int32.Parse(idxStr).GetValueOrDefault(); newTargetName.RemoveToEnd(bracketPos); } Debug.Assert(target != null); var targetFieldInfo = target.GetType().GetField(newTargetName); targetFieldInfo.Value.GetValue(target, out target); if (idx != -1) { if (var iList = target as IList) { var vari = iList[idx]; Debug.Assert(vari.IsObject); target = vari.Get(); } } Debug.Assert(target != null); } PropEntry propEntry = new PropEntry(); propEntry.mFlags = flags; propEntry.mPropertyName = new String(usePropName); propEntry.mTarget = target; propEntry.mListViewItem = item; propEntries[propIdx] = propEntry; //TODO: var fieldInfo = target.GetType().GetField(usePropName).Value; propEntry.mFieldInfo = fieldInfo; var curValue = fieldInfo.GetValue(target).GetValueOrDefault(); propEntry.mOrigValue = curValue; propEntry.mCurValue = DuplicateVariant(curValue); //TODO: Duplicate optionValues if (useOptionValues != null) { // Duplicate onto heap var prevOptionValues = useOptionValues; useOptionValues = new String[useOptionValues.Count]; for (int32 i = 0; i < prevOptionValues.Count; i++) useOptionValues[i] = new String(prevOptionValues[i]); } propEntry.mOptionValues = useOptionValues; bool handledMouseDown = false; var fieldType = fieldInfo.FieldType; if ((fieldType == typeof(bool)) && (useOptionValues == null)) { propEntry.mOptionValues = new String[] ( new String("No"), new String("Yes") ); } if (fieldType.IsNullable) { fieldType = ((SpecializedGenericType)fieldType).GetGenericArg(0); } if ((fieldType.IsEnum) || (fieldType == typeof(bool))) { if (propIdx == 0) { var comboBox = new DarkComboBox(); comboBox.mFrameKind = .Transparent; comboBox.mPopulateMenuAction.Add(new (menu) => { PopulateComboBox(menu, propEntries); }); subItem.AddWidget(comboBox); subItem.mOnResized.Add(new (evt) => { comboBox.Resize(0, 0, GetValueEditWidth(subItem), subItem.mHeight + 1); }); propEntry.mComboBoxes = new List(); propEntry.mComboBoxes.Add(comboBox); subItem.mOnMouseDown.Add(new (evt) => { comboBox.MouseDown(-1, -1, 0, 1); //comboBox.ShowDropdown(); }); } handledMouseDown = true; } else if (fieldType == typeof(List)) // List { item.MakeParent(); } if (!handledMouseDown) { if (propIdx == 0) subItem.mOnMouseDown.Add(new => PropValueClicked); } if ((flags.HasFlag(.BrowseForFile)) | (flags.HasFlag(.BrowseForFolder))) { propEntry.mEditInsets = new .(); DarkButton browseButton = new DarkButton(); browseButton.Label = "..."; browseButton.mOnMouseClick.Add(new (evt) => { let path = propEntry.mCurValue.Get(); if (propEntry.mFlags.HasFlag(.BrowseForFile)) { BrowseForFile(path); } else { BrowseForFolder(path); } propEntry.mListViewItem.GetSubItem(1).Label = path; CheckForChanges(); }); subItem.mOnResized.Add(new (evt) => { let width = GetValueEditWidth(subItem); propEntry.mEditInsets.mRight = GS!(22); browseButton.mLabelYOfs = GS!(-2); browseButton.Resize(width - GS!(21), GS!(1), GS!(20), subItem.mHeight - 2); }); subItem.AddWidget(browseButton); } } mPropPage.mPropEntries[item] = propEntries; UpdatePropertyValue(propEntries); return propEntries[0]; } protected (DarkListViewItem, PropEntry) AddPropertiesItem(DarkListViewItem parent, String name, String propName = null, String[] optionValues = null, PropEntry.Flags flags = .None) { var item = (DarkListViewItem)parent.CreateChildItem(); item.Label = name; item.mFocusColor = Color.Mult(DarkTheme.COLOR_TEXT, 0xFFA0A0A0); item.mOnMouseDown.Add(new => PropValueClicked); let propEntry = SetupPropertiesItem(item, name, propName, optionValues, flags); return (item, propEntry); } protected void PropValueClicked(MouseEvent theEvent) { DarkListViewItem clickedItem = (DarkListViewItem)theEvent.mSender; if (clickedItem.mColumnIdx == 0) { clickedItem.mListView.SetFocus(); clickedItem.mListView.GetRoot().SelectItemExclusively(clickedItem); return; } if (theEvent.mX != -1) { clickedItem.mListView.GetRoot().SelectItemExclusively(null); } DarkListViewItem item = (DarkListViewItem)clickedItem; DarkListViewItem rootItem = (DarkListViewItem)item.GetSubItem(0); PropEntry[] propertyEntries = mPropPage.mPropEntries[rootItem]; if (propertyEntries[0].mDisabled) return; EditValue(item, propertyEntries); } protected void ArrayPropValueClicked(PropEntry[] propEntries, int32 idx) { var propEntry = propEntries[0]; DarkListViewItem parentItem = propEntry.mListViewItem; DarkListViewItem clickedItem = (DarkListViewItem)parentItem.GetChildAtIndex(idx); DarkListViewItem item = (DarkListViewItem)clickedItem.GetSubItem(1); EditValue(item, propEntries, idx); } public override void DrawAll(Graphics g) { base.DrawAll(g); if (mCategorySelector.mVisible) IDEUtils.DrawOutline(g, mCategorySelector); IDEUtils.DrawOutline(g, mPropPage.mPropertiesListView, 0, 1); if (mSearchEdit != null) { g.Draw(DarkTheme.sDarkTheme.GetImage(.Search), mSearchEdit.mX + GS!(2), mSearchEdit.mY + GS!(1)); } } public override void Update() { base.Update(); if (mSearchEdit != null) { int curVersion = mSearchEdit.mEditWidgetContent.mData.mCurTextVersionId; if (curVersion != mSearchEdit.mSearchVersion) { UpdateSearch(); mSearchEdit.mSearchVersion = curVersion; } } } } }