From ec930120b841af5d9a7ff1c0ee80111b8965b16b Mon Sep 17 00:00:00 2001 From: John Lindal Date: Fri, 19 Jan 2024 22:50:24 -0800 Subject: [PATCH] jx_layout_editor: improve code generation --- todo-jxlayout | 4 ++ tools/jx_layout_editor/code/BaseWidget.cpp | 47 +++++++++++++++- tools/jx_layout_editor/code/BaseWidget.h | 6 ++- tools/jx_layout_editor/code/CustomWidget.cpp | 54 ++++++++++++++++++- tools/jx_layout_editor/code/CustomWidget.h | 2 + .../code/CustomWidgetPanel.cpp | 24 +++++++-- .../jx_layout_editor/code/CustomWidgetPanel.h | 7 +-- .../code/CustomWidgetPanel.jxl | 42 +++++++++++++-- .../jx_layout_editor/code/LayoutContainer.cpp | 48 +++++++++++++++-- tools/jx_layout_editor/code/LayoutContainer.h | 2 +- .../jx_layout_editor/code/LayoutDocument.cpp | 5 +- tools/jx_layout_editor/code/ScrollbarSet.cpp | 28 +++++++++- tools/jx_layout_editor/code/ScrollbarSet.h | 3 ++ tools/jx_layout_editor/code/fileVersions.h | 4 +- .../strings/CustomWidgetPanel_layout | 2 + tools/jx_layout_editor/strings/strings | 2 + 16 files changed, 256 insertions(+), 24 deletions(-) diff --git a/todo-jxlayout b/todo-jxlayout index b0e40373c..e18abd809 100644 --- a/todo-jxlayout +++ b/todo-jxlayout @@ -4,6 +4,10 @@ when show/hide toolbar, change window size to preserve LayoutContainer size snap-to-grid for bottom & right is offset +LayoutConfigDialog: + SetWMClass + SetMinSize/SetMaxSize/etc + ----- JXImageWidget diff --git a/tools/jx_layout_editor/code/BaseWidget.cpp b/tools/jx_layout_editor/code/BaseWidget.cpp index 84ae24aeb..c05c8c3b4 100644 --- a/tools/jx_layout_editor/code/BaseWidget.cpp +++ b/tools/jx_layout_editor/code/BaseWidget.cpp @@ -226,7 +226,7 @@ BaseWidget::EditConfiguration ******************************************************************************/ -void +bool BaseWidget::GenerateCode ( std::ostream& output, @@ -237,6 +237,11 @@ BaseWidget::GenerateCode ) const { + if (WaitForCodeDependency(*objNames)) + { + return false; + } + indent.Print(output); if (itsIsMemberVarFlag) { @@ -276,6 +281,46 @@ BaseWidget::GenerateCode PrintConfiguration(output, indent, itsVarName, stringdb); output << std::endl; + return true; +} + +/****************************************************************************** + PrepareToGenerateCode (virtual) + + ******************************************************************************/ + +void +BaseWidget::PrepareToGenerateCode() + const +{ +} + +/****************************************************************************** + GenerateCodeFinished (virtual) + + ******************************************************************************/ + +void +BaseWidget::GenerateCodeFinished() + const +{ +} + +/****************************************************************************** + WaitForCodeDependency (virtual protected) + + Return true if need to wait for another object to be created first. + + ******************************************************************************/ + +bool +BaseWidget::WaitForCodeDependency + ( + const JPtrArray& objNames + ) + const +{ + return false; } /****************************************************************************** diff --git a/tools/jx_layout_editor/code/BaseWidget.h b/tools/jx_layout_editor/code/BaseWidget.h index 9deb46796..cde02dcbe 100644 --- a/tools/jx_layout_editor/code/BaseWidget.h +++ b/tools/jx_layout_editor/code/BaseWidget.h @@ -47,11 +47,14 @@ class BaseWidget : public JXWidget JPoint GetDragStartPointGlobal() const; void EditConfiguration(const bool createUndo = true); - void GenerateCode(std::ostream& output, const JString& indent, + bool GenerateCode(std::ostream& output, const JString& indent, JPtrArray* objTypes, JPtrArray* objNames, JStringManager* stringdb) const; + virtual void PrepareToGenerateCode() const; + virtual void GenerateCodeFinished() const; + void PrepareToAcceptDrag(); virtual JString GetEnclosureName() const; @@ -71,6 +74,7 @@ class BaseWidget : public JXWidget const JString& indent, const JString& varName, JStringManager* stringdb) const; + virtual bool WaitForCodeDependency(const JPtrArray& objNames) const; virtual void AddPanels(WidgetParametersDialog* dlog); virtual void SavePanelData(); diff --git a/tools/jx_layout_editor/code/CustomWidget.cpp b/tools/jx_layout_editor/code/CustomWidget.cpp index 5618947b4..a49198935 100644 --- a/tools/jx_layout_editor/code/CustomWidget.cpp +++ b/tools/jx_layout_editor/code/CustomWidget.cpp @@ -74,6 +74,11 @@ CustomWidget::CustomWidget { input >> itsClassName >> itsCtorArgs >> itsCreateFlag; + if (vers >= 2) + { + input >> itsDependencyNames; + } + CustomWidgetX(); } @@ -113,6 +118,7 @@ CustomWidget::StreamOut output << itsClassName << std::endl; output << itsCtorArgs << std::endl; output << itsCreateFlag << std::endl; + output << itsDependencyNames << std::endl; } /****************************************************************************** @@ -225,6 +231,49 @@ CustomWidget::PrintCtorArgsWithComma } } +/****************************************************************************** + WaitForCodeDependency (virtual protected) + + Return true if need to wait for another object to be created first. + + ******************************************************************************/ + +bool +CustomWidget::WaitForCodeDependency + ( + const JPtrArray& objNames + ) + const +{ + if (itsDependencyNames.IsEmpty()) + { + return false; + } + + JPtrArray list(JPtrArrayT::kDeleteAll); + itsDependencyNames.Split(",", &list); + + for (auto* d : list) + { + bool found = false; + for (auto* n : objNames) + { + if (*n == *d) + { + found = true; + break; + } + } + + if (!found) + { + return true; + } + } + + return false; +} + /****************************************************************************** AddPanels (virtual protected) @@ -238,7 +287,7 @@ CustomWidget::AddPanels { itsPanel = jnew CustomWidgetPanel(dlog, itsClassName, itsCtorArgs, itsCreateFlag, - WantsInput()); + itsDependencyNames, WantsInput()); } /****************************************************************************** @@ -250,7 +299,8 @@ void CustomWidget::SavePanelData() { bool wantsInput; - itsPanel->GetValues(&itsClassName, &itsCtorArgs, &itsCreateFlag, &wantsInput); + itsPanel->GetValues(&itsClassName, &itsCtorArgs, &itsCreateFlag, + &itsDependencyNames, &wantsInput); SetWantsInput(wantsInput); itsPanel = nullptr; } diff --git a/tools/jx_layout_editor/code/CustomWidget.h b/tools/jx_layout_editor/code/CustomWidget.h index 6a455e41d..99c862dbb 100644 --- a/tools/jx_layout_editor/code/CustomWidget.h +++ b/tools/jx_layout_editor/code/CustomWidget.h @@ -45,6 +45,7 @@ class CustomWidget : public BaseWidget void PrintCtorArgsWithComma(std::ostream& output, const JString& varName, JStringManager* stringdb) const override; + bool WaitForCodeDependency(const JPtrArray& objNames) const override; void AddPanels(WidgetParametersDialog* dlog) override; void SavePanelData() override; @@ -54,6 +55,7 @@ class CustomWidget : public BaseWidget JString itsClassName; JString itsCtorArgs; bool itsCreateFlag; + JString itsDependencyNames; CustomWidgetPanel* itsPanel; // nullptr unless editing diff --git a/tools/jx_layout_editor/code/CustomWidgetPanel.cpp b/tools/jx_layout_editor/code/CustomWidgetPanel.cpp index 3c5f0fc95..d66935550 100644 --- a/tools/jx_layout_editor/code/CustomWidgetPanel.cpp +++ b/tools/jx_layout_editor/code/CustomWidgetPanel.cpp @@ -30,10 +30,11 @@ CustomWidgetPanel::CustomWidgetPanel const JString& className, const JString& ctorArgs, const bool needsCreate, + const JString& deps, const bool wantsInput ) { - BuildPanel(dlog, className, ctorArgs, needsCreate, wantsInput); + BuildPanel(dlog, className, ctorArgs, needsCreate, deps, wantsInput); } /****************************************************************************** @@ -58,6 +59,7 @@ CustomWidgetPanel::BuildPanel const JString& className, const JString& ctorArgs, const bool needsCreate, + const JString& deps, const bool wantsInput ) { @@ -67,7 +69,7 @@ CustomWidgetPanel::BuildPanel auto* container = jnew JXWidgetSet(window, - JXWidget::kFixedLeft, JXWidget::kFixedTop, 0,0, 460,100); + JXWidget::kFixedLeft, JXWidget::kFixedTop, 0,0, 460,130); assert( container != nullptr ); auto* classNameLabel = @@ -80,14 +82,19 @@ CustomWidgetPanel::BuildPanel JXWidget::kFixedLeft, JXWidget::kFixedTop, 20,39, 80,20); constructorArgsLabel->SetToLabel(false); + auto* dependencyLabel = + jnew JXStaticText(JGetString("dependencyLabel::CustomWidgetPanel::Panel"),container, + JXWidget::kFixedLeft, JXWidget::kFixedTop, 20,70, 80,20); + dependencyLabel->SetToLabel(false); + itsWantsInputCB = jnew JXTextCheckbox(JGetString("itsWantsInputCB::CustomWidgetPanel::Panel"), container, - JXWidget::kFixedLeft, JXWidget::kFixedTop, 20,70, 160,20); + JXWidget::kFixedLeft, JXWidget::kFixedTop, 20,100, 160,20); itsWantsInputCB->SetShortcuts(JGetString("itsWantsInputCB::shortcuts::CustomWidgetPanel::Panel")); itsNeedsCreateCB = jnew JXTextCheckbox(JGetString("itsNeedsCreateCB::CustomWidgetPanel::Panel"), container, - JXWidget::kFixedLeft, JXWidget::kFixedTop, 280,70, 160,20); + JXWidget::kFixedLeft, JXWidget::kFixedTop, 280,100, 160,20); itsNeedsCreateCB->SetShortcuts(JGetString("itsNeedsCreateCB::shortcuts::CustomWidgetPanel::Panel")); itsClassNameInput = @@ -98,6 +105,11 @@ CustomWidgetPanel::BuildPanel jnew JXInputField(container, JXWidget::kFixedLeft, JXWidget::kFixedTop, 100,40, 340,20); + itsDependencyInput = + jnew JXInputField(container, + JXWidget::kFixedLeft, JXWidget::kFixedTop, 100,70, 340,20); + itsDependencyInput->SetValidationPattern(jnew JRegex("^[_a-z][_a-z0-9,]+$", "i"), "itsDependencyInput::validation::CustomWidgetPanel::Panel"); + // end Panel dlog->AddPanel(this, container); @@ -111,6 +123,8 @@ CustomWidgetPanel::BuildPanel itsCtorArgs->GetText()->SetText(ctorArgs); itsNeedsCreateCB->SetState(needsCreate); + itsDependencyInput->GetText()->SetText(deps); + itsWantsInputCB->SetState(wantsInput); } @@ -125,11 +139,13 @@ CustomWidgetPanel::GetValues JString* className, JString* ctorArgs, bool* needsCreate, + JString* deps, bool* wantsInput ) { *className = itsClassNameInput->GetText()->GetText(); *ctorArgs = itsCtorArgs->GetText()->GetText(); + *deps = itsDependencyInput->GetText()->GetText(); *needsCreate = itsNeedsCreateCB->IsChecked(); *wantsInput = itsWantsInputCB->IsChecked(); } diff --git a/tools/jx_layout_editor/code/CustomWidgetPanel.h b/tools/jx_layout_editor/code/CustomWidgetPanel.h index 195a77c5e..56fc6a861 100644 --- a/tools/jx_layout_editor/code/CustomWidgetPanel.h +++ b/tools/jx_layout_editor/code/CustomWidgetPanel.h @@ -20,12 +20,12 @@ class CustomWidgetPanel : public WidgetPanelBase CustomWidgetPanel(WidgetParametersDialog* dlog, const JString& className, const JString& ctorArgs, const bool needsCreate, - const bool wantsInput); + const JString& deps, const bool wantsInput); ~CustomWidgetPanel(); void GetValues(JString* className, JString* ctorArgs, bool* needsCreate, - bool* wantsInput); + JString* deps, bool* wantsInput); private: @@ -35,6 +35,7 @@ class CustomWidgetPanel : public WidgetPanelBase JXTextCheckbox* itsNeedsCreateCB; JXInputField* itsClassNameInput; JXInputField* itsCtorArgs; + JXInputField* itsDependencyInput; // end Panel @@ -42,7 +43,7 @@ class CustomWidgetPanel : public WidgetPanelBase void BuildPanel(WidgetParametersDialog* dlog, const JString& className, const JString& ctorArgs, const bool needsCreate, - const bool wantsInput); + const JString& deps, const bool wantsInput); }; #endif diff --git a/tools/jx_layout_editor/code/CustomWidgetPanel.jxl b/tools/jx_layout_editor/code/CustomWidgetPanel.jxl index d21f8ee6a..f6c86891e 100644 --- a/tools/jx_layout_editor/code/CustomWidgetPanel.jxl +++ b/tools/jx_layout_editor/code/CustomWidgetPanel.jxl @@ -1,6 +1,6 @@ jx_layout_editor 1 460 -100 +130 "Panel" "" "window" @@ -11,7 +11,7 @@ jx_layout_editor 1 "WidgetSet" 0 0 -0 0 100 460 +0 0 130 460 "container" 0 0 @@ -84,12 +84,46 @@ jx_layout_editor 1 0 0 +1 +1 +"StaticText" +0 +0 +70 20 90 100 +"dependencyLabel" +0 +0 +"Depends on:" +1 +0 +0 +0 +0 + +1 +1 +"InputField" +0 +0 +70 100 90 440 +"itsDependencyInput" +1 +3 +0 +0 +0 +"^[_a-z][_a-z0-9,]+$" +"i" +"The dependencies must be valid C++ identifiers, separated by commas." +0 +0 + 1 1 "TextCheckbox" 0 0 -70 20 90 180 +100 20 120 180 "itsWantsInputCB" 1 0 @@ -101,7 +135,7 @@ jx_layout_editor 1 "TextCheckbox" 0 0 -70 280 90 440 +100 280 120 440 "itsNeedsCreateCB" 1 0 diff --git a/tools/jx_layout_editor/code/LayoutContainer.cpp b/tools/jx_layout_editor/code/LayoutContainer.cpp index cf0d61478..27b8125b0 100644 --- a/tools/jx_layout_editor/code/LayoutContainer.cpp +++ b/tools/jx_layout_editor/code/LayoutContainer.cpp @@ -656,11 +656,11 @@ LayoutContainer::WriteWidget } /****************************************************************************** - GenerateCode (private) + GenerateCode ******************************************************************************/ -void +bool LayoutContainer::GenerateCode ( std::ostream& output, @@ -725,6 +725,7 @@ LayoutContainer::GenerateCode return; } + widget->PrepareToGenerateCode(); if (widget->WantsInput()) { inputWidgets.Append(const_cast(widget)); @@ -741,9 +742,31 @@ LayoutContainer::GenerateCode otherWidgets.SetCompareFunction(CompareLocations); otherWidgets.Sort(); - for (auto* widget: otherWidgets) + JPtrArrayIterator iter(&otherWidgets); + while (!otherWidgets.IsEmpty()) { - widget->GenerateCode(output, indent, objTypes, objNames, stringdb); + const JSize origCount = otherWidgets.GetItemCount(); + + iter.MoveTo(JListT::kStartAtBeginning, 0); + BaseWidget* widget; + while (iter.Next(&widget)) + { + if (widget->GenerateCode(output, indent, objTypes, objNames, stringdb)) + { + iter.RemovePrev(); + } + else + { + otherWidgets.MoveItemToIndex(1, otherWidgets.GetItemCount()); + } + } + + if (otherWidgets.GetItemCount() == origCount) + { + JGetUserNotification()->ReportError( + JString("CircularDependency::LayoutContainer")); + return false; + } } // ensure tab order is maintained @@ -753,9 +776,22 @@ LayoutContainer::GenerateCode for (auto* widget: inputWidgets) { - widget->GenerateCode(output, indent, objTypes, objNames, stringdb); + const bool ok = widget->GenerateCode(output, indent, objTypes, objNames, stringdb); + assert( ok ); } + // clean up + + ForEach([](const JXContainer* obj) + { + auto* widget = dynamic_cast(obj); + if (widget != nullptr) + { + widget->GenerateCodeFinished(); + } + }, + true); + // reset enclosure size if (itsAdjustContainerToFitFlag) @@ -769,6 +805,8 @@ LayoutContainer::GenerateCode output << ".height());" << std::endl; output << std::endl; } + + return true; } /****************************************************************************** diff --git a/tools/jx_layout_editor/code/LayoutContainer.h b/tools/jx_layout_editor/code/LayoutContainer.h index 267b9d1a9..e17cb09ea 100644 --- a/tools/jx_layout_editor/code/LayoutContainer.h +++ b/tools/jx_layout_editor/code/LayoutContainer.h @@ -58,7 +58,7 @@ class LayoutContainer : public JXWidget void AppendToToolBar(JXToolBar* toolBar) const; - void GenerateCode(std::ostream& output, const JString& indent, + bool GenerateCode(std::ostream& output, const JString& indent, JPtrArray* objTypes, JPtrArray* objNames, JStringManager* stringdb) const; diff --git a/tools/jx_layout_editor/code/LayoutDocument.cpp b/tools/jx_layout_editor/code/LayoutDocument.cpp index c1b982f43..c371465af 100644 --- a/tools/jx_layout_editor/code/LayoutDocument.cpp +++ b/tools/jx_layout_editor/code/LayoutDocument.cpp @@ -664,7 +664,10 @@ LayoutDocument::GenerateCode() JPtrArray objTypes(JPtrArrayT::kDeleteAll), objNames(JPtrArrayT::kDeleteAll); JStringManager stringdb; - itsLayout->GenerateCode(sourceOutput, indent, &objTypes, &objNames, &stringdb); + if (!itsLayout->GenerateCode(sourceOutput, indent, &objTypes, &objNames, &stringdb)) + { + return false; + } bool done = CopyAfterCodeDelimiter(sourceInput, sourceOutput); sourceInput.close(); diff --git a/tools/jx_layout_editor/code/ScrollbarSet.cpp b/tools/jx_layout_editor/code/ScrollbarSet.cpp index af39a97ad..212a2d517 100644 --- a/tools/jx_layout_editor/code/ScrollbarSet.cpp +++ b/tools/jx_layout_editor/code/ScrollbarSet.cpp @@ -13,6 +13,8 @@ #include #include +const JCoordinate kScrollbarWidth = 15; + /****************************************************************************** Constructor @@ -62,7 +64,7 @@ ScrollbarSet::ScrollbarSetX { itsLayout = jnew LayoutContainer(layout, this, this, kHElastic, kVElastic, 0,0, 100,100); itsLayout->FitToEnclosure(); - itsLayout->AdjustSize(-15, -15); + itsLayout->AdjustSize(-kScrollbarWidth, -kScrollbarWidth); } /****************************************************************************** @@ -164,3 +166,27 @@ ScrollbarSet::GetEnclosureName() bool b; return GetVarName(&b) + "->GetScrollEnclosure()"; } + +/****************************************************************************** + PrepareToGenerateCode (virtual) + + ******************************************************************************/ + +void +ScrollbarSet::PrepareToGenerateCode() + const +{ + itsLayout->AdjustSize(kScrollbarWidth, kScrollbarWidth); +} + +/****************************************************************************** + GenerateCodeFinished (virtual) + + ******************************************************************************/ + +void +ScrollbarSet::GenerateCodeFinished() + const +{ + itsLayout->AdjustSize(-kScrollbarWidth, -kScrollbarWidth); +} diff --git a/tools/jx_layout_editor/code/ScrollbarSet.h b/tools/jx_layout_editor/code/ScrollbarSet.h index a80c83e08..8ed8ec130 100644 --- a/tools/jx_layout_editor/code/ScrollbarSet.h +++ b/tools/jx_layout_editor/code/ScrollbarSet.h @@ -29,6 +29,9 @@ class ScrollbarSet : public BaseWidget bool GetLayoutContainer(LayoutContainer** layout) const override; JString GetEnclosureName() const override; + void PrepareToGenerateCode() const override; + void GenerateCodeFinished() const override; + protected: void Draw(JXWindowPainter& p, const JRect& rect) override; diff --git a/tools/jx_layout_editor/code/fileVersions.h b/tools/jx_layout_editor/code/fileVersions.h index e1cbb637e..2c00a4152 100644 --- a/tools/jx_layout_editor/code/fileVersions.h +++ b/tools/jx_layout_editor/code/fileVersions.h @@ -12,8 +12,10 @@ #include -const JFileVersion kCurrentFileVersion = 1; +const JFileVersion kCurrentFileVersion = 2; +// version 2: +// adds itsDependencyNames to CustomWidget // version 1: // adds itsValidationFlags to InputField diff --git a/tools/jx_layout_editor/strings/CustomWidgetPanel_layout b/tools/jx_layout_editor/strings/CustomWidgetPanel_layout index 33eab8626..b5e292a53 100644 --- a/tools/jx_layout_editor/strings/CustomWidgetPanel_layout +++ b/tools/jx_layout_editor/strings/CustomWidgetPanel_layout @@ -1,10 +1,12 @@ 0 itsNeedsCreateCB::CustomWidgetPanel::Panel "Construct via Create()" itsWantsInputCB::shortcuts::CustomWidgetPanel::Panel "#K" +dependencyLabel::CustomWidgetPanel::Panel "Depends on:" itsWantsInputCB::CustomWidgetPanel::Panel "Wants keyboard input" constructorArgsLabel::CustomWidgetPanel::Panel "Ctor args:" classNameLabel::CustomWidgetPanel::Panel "Class name:" itsNeedsCreateCB::shortcuts::CustomWidgetPanel::Panel "#O" +itsDependencyInput::validation::CustomWidgetPanel::Panel "The dependencies must be valid C++ identifiers, separated by commas." # This file was automatically generated by jx_layout_editor. diff --git a/tools/jx_layout_editor/strings/strings b/tools/jx_layout_editor/strings/strings index d6d1c2bd3..c6d8666d2 100644 --- a/tools/jx_layout_editor/strings/strings +++ b/tools/jx_layout_editor/strings/strings @@ -91,6 +91,8 @@ ContainerNameMustBeValidIdentifier::LayoutConfigDialog "The container name must VarNameBase::LayoutContainer "widget_" +CircularDependency::LayoutContainer "Code generation failed because there is a circular dependency between widgets." + # ChooseWidgetDialog WindowTitle::ChooseWidgetDialog "Select Widget"