diff --git a/engine/equations.cc b/engine/equations.cc index 8ce54e223..c6ff465c1 100644 --- a/engine/equations.cc +++ b/engine/equations.cc @@ -535,7 +535,7 @@ namespace MathDAG { // slightly convoluted to prevent sliderSet from overriding c->value iv->init(c->value); - iv->adjustSliderBounds(); + if (auto vv=iv->vValue()) vv->adjustSliderBounds(); } else throw error("only constants, parameters and variables can be connected to the initial value port"); diff --git a/engine/mdlReader.cc b/engine/mdlReader.cc index aa6e842c3..643e37e60 100644 --- a/engine/mdlReader.cc +++ b/engine/mdlReader.cc @@ -366,24 +366,25 @@ namespace minsky } catch (...) {} v->detailedText(comments); - if (regex_match(sliderSpec,match,sliderSpecPattern)) + if (auto vv=v->vValue()) { - vector spec; - for (size_t i=1; i<=match.size(); ++i) spec.push_back(match[i]); - if (!spec.empty() && regex_match(spec[0],match,number)) - v->sliderMin=stod(spec[0]); - else - v->sliderMin=0.1*stod(definition); - if (spec.size()>1 && regex_match(spec[1],match,number)) - v->sliderMax=stod(spec[1]); - else - v->sliderMax=10*stod(definition); - if (spec.size()>2 && regex_match(spec[2],match,number)) - v->sliderStep=stod(spec[2]); - v->adjustSliderBounds(); + if (regex_match(sliderSpec,match,sliderSpecPattern)) + { + vector spec; + for (size_t i=1; i<=match.size(); ++i) spec.push_back(match[i]); + if (!spec.empty() && regex_match(spec[0],match,number)) + vv->sliderMin=stod(spec[0]); + else + vv->sliderMin=0.1*stod(definition); + if (spec.size()>1 && regex_match(spec[1],match,number)) + vv->sliderMax=stod(spec[1]); + else + vv->sliderMax=10*stod(definition); + if (spec.size()>2 && regex_match(spec[2],match,number)) + vv->sliderStep=stod(spec[2]); + } + vv->adjustSliderBounds(); } - else - v->initSliderBounds(); } else { diff --git a/model/slider.h b/engine/slider.h similarity index 70% rename from model/slider.h rename to engine/slider.h index 29ac73433..08d3870b3 100644 --- a/model/slider.h +++ b/engine/slider.h @@ -27,12 +27,18 @@ namespace minsky /// operations and variables struct Slider { - mutable bool sliderBoundsSet=false, ///< slider bounds have been initialised at some point - sliderStepRel=false; /**< sliderStep is relative to the range + bool sliderStepRel=false; /**< sliderStep is relative to the range [sliderMin,sliderMax] */ - mutable double sliderMin, sliderMax, sliderStep; + double sliderMin=std::numeric_limits::max(), + sliderMax=-sliderMin, sliderStep=0; bool enableSlider=true; + + /// ensure there are at most 10000 steps between sliderMin and Max. see ticket 1255. + double maxSliderSteps() const { + if (!isfinite(sliderStep) || sliderMax-sliderMin > 1.0e04*sliderStep) return (sliderMax-sliderMin)/1.0e04; + return sliderStep; + } }; } diff --git a/engine/variableSummary.h b/engine/variableSummary.h index fbeed11af..5429c8b41 100644 --- a/engine/variableSummary.h +++ b/engine/variableSummary.h @@ -33,6 +33,7 @@ namespace minsky std::string definition; ///< LaTeXable definition std::string udfDefinition; ///< use function compatible definition std::string init; ///< initial value + double sliderStep, sliderMin, sliderMax; ///< slider parameters double value=nan(""); ///< value, if scalar std::string scope; ///< name, id of scope if local, ":" if global std::string godley; ///< name or id of Godley table, if contained in a Godley table diff --git a/engine/variableValue.cc b/engine/variableValue.cc index 10fd07c36..5c7e5e980 100644 --- a/engine/variableValue.cc +++ b/engine/variableValue.cc @@ -29,6 +29,7 @@ #include "index.rcd" #include "index.xcd" #include "nobble.h" +#include "slider.rcd" #include "tensorInterface.rcd" #include "tensorInterface.xcd" #include "tensorVal.rcd" @@ -214,10 +215,37 @@ namespace minsky if (rhs) return rhs->hypercube(); // return the initialisation hypercube if not a defined flowVar if (tensorInit.rank()>0 && (!isFlowVar() || m_type==parameter || !cminsky().definingVar(valueId()))) - return tensorInit.hypercube(); + return tensorInit.hypercube(); return m_hypercube; } + void VariableValue::sliderSet(double x) + { + if (!isfinite(x)) return; + if (xsliderMax) x=sliderMax; + sliderStep=maxSliderSteps(); + init(to_string(x)); + setValue(x); + } + + void VariableValue::incrSlider(double step) + { + sliderSet(value()+step*(sliderStepRel? value(): 1)*sliderStep); + } + + void VariableValue::adjustSliderBounds() + { + if (size()==1 && !isnan(value())) + { + if (!finite(sliderMax) ||sliderMax=value()) + sliderMin=value()? -abs(10*value()):-1; + assert(sliderMin0 && e>x && *e) { x=e+1; - dims.push_back(tmp); + dims.push_back(tmp); } else break; @@ -300,27 +328,27 @@ namespace minsky void VariableValues::resetValue(VariableValue& v) const { if (v.idx()<0) v.allocValue(); - // initialise variable only if its variable is not defined or it is a stock - if (!v.isFlowVar() || !cminsky().definingVar(v.valueId())) - { - if (v.tensorInit.rank()) - { - // ensure dimensions are correct - auto hc=v.tensorInit.hypercube(); - for (auto& xv: hc.xvectors) - { - auto dim=cminsky().dimensions.find(xv.name); - if (dim!=cminsky().dimensions.end()) - xv.dimension=dim->second; - } - v.tensorInit.hypercube(hc); - } - if (v.tensorInit.rank()) - v=v.tensorInit; - else - v=initValue(v); - } - assert(v.idxInRange()); + // initialise variable only if its variable is not defined or it is a stock + if (!v.isFlowVar() || !cminsky().definingVar(v.valueId())) + { + if (v.tensorInit.rank()) + { + // ensure dimensions are correct + auto hc=v.tensorInit.hypercube(); + for (auto& xv: hc.xvectors) + { + auto dim=cminsky().dimensions.find(xv.name); + if (dim!=cminsky().dimensions.end()) + xv.dimension=dim->second; + } + v.tensorInit.hypercube(hc); + } + if (v.tensorInit.rank()) + v=v.tensorInit; + else + v=initValue(v); + } + assert(v.idxInRange()); } @@ -344,7 +372,7 @@ namespace minsky resetValue(v.second->allocValue()); assert(v.second->idxInRange()); } -} + } bool VariableValues::validEntries() const { @@ -533,6 +561,7 @@ namespace minsky definition, udfDefinition, init(), + sliderStep, sliderMin, sliderMax, value(), scopeName, godleyName, diff --git a/engine/variableValue.h b/engine/variableValue.h index 8ec661a67..19ca62f20 100644 --- a/engine/variableValue.h +++ b/engine/variableValue.h @@ -26,6 +26,7 @@ #include "str.h" #include "CSVDialog.h" #include "latexMarkup.h" +#include "slider.h" #include "valueId.h" #include "variableSummary.h" #include @@ -41,7 +42,7 @@ namespace minsky typedef std::shared_ptr GroupPtr; using namespace civita; - struct VariableValueData: public civita::ITensorVal + struct VariableValueData: public civita::ITensorVal, public Slider { using ITensorVal::operator=; @@ -58,8 +59,6 @@ namespace minsky /// long and short descriptions - common to all variables of a given name std::string detailedText, tooltip; - - bool sliderVisible=false; // determined at reset time bool godleyOverridden=false; std::string name; // name of this variable classdesc::Exclude> m_scope; @@ -186,6 +185,14 @@ namespace minsky const std::string& init() const {return m_init;} const std::string& init(const std::string& x); + + /// sets variable value (or init value) + void sliderSet(double x); + /// increment slider by \a step + void incrSlider(double step); + + /// adjust slider bounds to encompass the current value + void adjustSliderBounds(); }; struct ValueVector diff --git a/gui-js/libs/shared/src/lib/backend/minsky.ts b/gui-js/libs/shared/src/lib/backend/minsky.ts index 499c91c80..5c2e2967c 100644 --- a/gui-js/libs/shared/src/lib/backend/minsky.ts +++ b/gui-js/libs/shared/src/lib/backend/minsky.ts @@ -231,7 +231,7 @@ export class VariableBase extends Item { async drawSelected(a1: minsky__dummy): Promise {return this.$callMethod('drawSelected',a1);} async dummyDraw(): Promise {return this.$callMethod('dummyDraw');} async editorMode(): Promise {return this.$callMethod('editorMode');} - async enableSlider(...args: boolean[]): Promise {return this.$callMethod('enableSlider',...args);} + async enableSlider(...args: any[]): Promise {return this.$callMethod('enableSlider',...args);} async engExp(): Promise {return this.$callMethod('engExp');} async ensureBBValid(): Promise {return this.$callMethod('ensureBBValid');} async ensureValueExists(a1: VariableValue,a2: string): Promise {return this.$callMethod('ensureValueExists',a1,a2);} @@ -245,7 +245,6 @@ export class VariableBase extends Item { async importFromCSV(a1: string,a2: DataSpecSchema): Promise {return this.$callMethod('importFromCSV',a1,a2);} async inItem(a1: number,a2: number): Promise {return this.$callMethod('inItem',a1,a2);} async init(...args: any[]): Promise {return this.$callMethod('init',...args);} - async initSliderBounds(): Promise {return this.$callMethod('initSliderBounds');} async inputWired(): Promise {return this.$callMethod('inputWired');} async ioVar(): Promise {return this.$callMethod('ioVar');} async isStock(): Promise {return this.$callMethod('isStock');} @@ -259,7 +258,6 @@ export class VariableBase extends Item { async m_y(...args: number[]): Promise {return this.$callMethod('m_y',...args);} async makeConsistentWithValue(): Promise {return this.$callMethod('makeConsistentWithValue');} async mantissa(a1: EngNotation,a2: number): Promise {return this.$callMethod('mantissa',a1,a2);} - async maxSliderSteps(): Promise {return this.$callMethod('maxSliderSteps');} async miniPlotEnabled(...args: any[]): Promise {return this.$callMethod('miniPlotEnabled',...args);} async mouseFocus(...args: boolean[]): Promise {return this.$callMethod('mouseFocus',...args);} async moveTo(a1: number,a2: number): Promise {return this.$callMethod('moveTo',a1,a2);} @@ -293,12 +291,10 @@ export class VariableBase extends Item { async selected(...args: boolean[]): Promise {return this.$callMethod('selected',...args);} async setDimLabelsPicked(a1: string,a2: string): Promise {return this.$callMethod('setDimLabelsPicked',a1,a2);} async setUnits(a1: string): Promise {return this.$callMethod('setUnits',a1);} - async sliderBoundsSet(...args: boolean[]): Promise {return this.$callMethod('sliderBoundsSet',...args);} - async sliderMax(...args: number[]): Promise {return this.$callMethod('sliderMax',...args);} - async sliderMin(...args: number[]): Promise {return this.$callMethod('sliderMin',...args);} - async sliderSet(a1: number): Promise {return this.$callMethod('sliderSet',a1);} - async sliderStep(...args: number[]): Promise {return this.$callMethod('sliderStep',...args);} - async sliderStepRel(...args: boolean[]): Promise {return this.$callMethod('sliderStepRel',...args);} + async sliderMax(...args: any[]): Promise {return this.$callMethod('sliderMax',...args);} + async sliderMin(...args: any[]): Promise {return this.$callMethod('sliderMin',...args);} + async sliderStep(...args: any[]): Promise {return this.$callMethod('sliderStep',...args);} + async sliderStepRel(...args: any[]): Promise {return this.$callMethod('sliderStepRel',...args);} async sliderVisible(): Promise {return this.$callMethod('sliderVisible');} async temp(): Promise {return this.$callMethod('temp');} async throw_error(a1: string): Promise {return this.$callMethod('throw_error',a1);} @@ -2282,6 +2278,7 @@ export class VariableValue extends CppClass { this.tensorInit=new civita__TensorVal(this.$prefix()+'.tensorInit'); this.units=new Units(this.$prefix()+'.units'); } + async adjustSliderBounds(): Promise {return this.$callMethod('adjustSliderBounds');} async allocValue(): Promise {return this.$callMethod('allocValue');} async at(a1: number): Promise {return this.$callMethod('at',a1);} async atHCIndex(a1: number): Promise {return this.$callMethod('atHCIndex',a1);} @@ -2289,6 +2286,7 @@ export class VariableValue extends CppClass { async cancel(a1: boolean): Promise {return this.$callMethod('cancel',a1);} async data(): Promise {return this.$callMethod('data');} async detailedText(...args: string[]): Promise {return this.$callMethod('detailedText',...args);} + async enableSlider(...args: boolean[]): Promise {return this.$callMethod('enableSlider',...args);} async end(): Promise {return this.$callMethod('end');} async exportAsCSV(a1: string,a2: string,a3: boolean): Promise {return this.$callMethod('exportAsCSV',a1,a2,a3);} async godleyOverridden(...args: boolean[]): Promise {return this.$callMethod('godleyOverridden',...args);} @@ -2296,11 +2294,13 @@ export class VariableValue extends CppClass { async idx(): Promise {return this.$callMethod('idx');} async idxInRange(): Promise {return this.$callMethod('idxInRange');} async imposeDimensions(a1: Container>): Promise {return this.$callMethod('imposeDimensions',a1);} + async incrSlider(a1: number): Promise {return this.$callMethod('incrSlider',a1);} async index(...args: any[]): Promise {return this.$callMethod('index',...args);} async init(...args: any[]): Promise {return this.$callMethod('init',...args);} async isFlowVar(): Promise {return this.$callMethod('isFlowVar');} async isZero(): Promise {return this.$callMethod('isZero');} async lhs(): Promise {return this.$callMethod('lhs');} + async maxSliderSteps(): Promise {return this.$callMethod('maxSliderSteps');} async name(...args: string[]): Promise {return this.$callMethod('name',...args);} async rank(): Promise {return this.$callMethod('rank');} async reset_idx(): Promise {return this.$callMethod('reset_idx');} @@ -2310,7 +2310,11 @@ export class VariableValue extends CppClass { async setValue(...args: any[]): Promise {return this.$callMethod('setValue',...args);} async shape(): Promise {return this.$callMethod('shape');} async size(): Promise {return this.$callMethod('size');} - async sliderVisible(...args: boolean[]): Promise {return this.$callMethod('sliderVisible',...args);} + async sliderMax(...args: number[]): Promise {return this.$callMethod('sliderMax',...args);} + async sliderMin(...args: number[]): Promise {return this.$callMethod('sliderMin',...args);} + async sliderSet(a1: number): Promise {return this.$callMethod('sliderSet',a1);} + async sliderStep(...args: number[]): Promise {return this.$callMethod('sliderStep',...args);} + async sliderStepRel(...args: boolean[]): Promise {return this.$callMethod('sliderStepRel',...args);} async summary(): Promise {return this.$callMethod('summary');} async temp(): Promise {return this.$callMethod('temp');} async tooltip(...args: string[]): Promise {return this.$callMethod('tooltip',...args);} diff --git a/gui-js/libs/ui-components/src/lib/create-variable/create-variable.component.ts b/gui-js/libs/ui-components/src/lib/create-variable/create-variable.component.ts index 11532ae1f..a7d6f01d2 100644 --- a/gui-js/libs/ui-components/src/lib/create-variable/create-variable.component.ts +++ b/gui-js/libs/ui-components/src/lib/create-variable/create-variable.component.ts @@ -184,7 +184,7 @@ export class CreateVariableComponent implements OnInit, OnDestroy { saveVariableParams(item: VariableBase) { item.setUnits(this.units.value || ''); item.init(this.value.value); - item.initSliderBounds(); // ensure slider bounds starts with a reasonable value + item.adjustSliderBounds(); // ensure slider bounds starts with a reasonable value item.rotation(this.rotation.value || 0); item.tooltip(this.shortDescription.value); item.detailedText(this.detailedDescription.value); diff --git a/gui-js/libs/ui-components/src/lib/summary/summary.component.html b/gui-js/libs/ui-components/src/lib/summary/summary.component.html index 8c5dce8ba..1598dfd9f 100644 --- a/gui-js/libs/ui-components/src/lib/summary/summary.component.html +++ b/gui-js/libs/ui-components/src/lib/summary/summary.component.html @@ -15,8 +15,11 @@
Name
Definition
Dimensions
-
Initial
Units
+
Initial
+
Step
+
Min
+
Max
Value
@@ -57,6 +60,22 @@ {{variable.dimensions.toString()}} +
+
+
+ +
+
+ class="data-cell">
-
-
+
- + +
+
+
+ +
+ +
+
+
+ +
+
+ + +
diff --git a/gui-js/libs/ui-components/src/lib/summary/summary.component.ts b/gui-js/libs/ui-components/src/lib/summary/summary.component.ts index 651163075..ff3733f8b 100644 --- a/gui-js/libs/ui-components/src/lib/summary/summary.component.ts +++ b/gui-js/libs/ui-components/src/lib/summary/summary.component.ts @@ -83,6 +83,10 @@ export class SummaryComponent implements OnInit { this.numVars[name]=this.godleys[name].length; this.numVars.godleys+=this.godleys[name].length; } + + // prevent wiring tab handlers from interfering with this tab + document.body.onkeydown=null; + document.body.onkeyup=null; } types(category: string): string[] { @@ -143,6 +147,15 @@ export class SummaryComponent implements OnInit { case 'init': this.electronService.minsky.variableValues.elem(this.editRow.valueId).init(this.editCellContents); break; + case 'sliderMin': + this.electronService.minsky.variableValues.elem(this.editRow.valueId).sliderMin(+this.editCellContents); + break; + case 'sliderMax': + this.electronService.minsky.variableValues.elem(this.editRow.valueId).sliderMax(+this.editCellContents); + break; + case 'sliderStep': + this.electronService.minsky.variableValues.elem(this.editRow.valueId).sliderStep(+this.editCellContents); + break; case 'units': this.electronService.minsky.variableValues.elem(this.editRow.valueId).setUnits(this.editCellContents); break; diff --git a/gui-js/libs/ui-components/src/lib/wiring/wiring.component.ts b/gui-js/libs/ui-components/src/lib/wiring/wiring.component.ts index e9168031a..9ac83a5e8 100644 --- a/gui-js/libs/ui-components/src/lib/wiring/wiring.component.ts +++ b/gui-js/libs/ui-components/src/lib/wiring/wiring.component.ts @@ -83,7 +83,7 @@ export class WiringComponent implements OnInit, OnDestroy { minskyCanvasContainer.scrollTop=scrollableArea.height/2 - pos[1]; }; this.cmService.resetScroll(); - + this.zone.runOutsideAngular(() => { if (this.electronService.isElectron) { const handleScroll = async (scrollTop: number, scrollLeft: number) => { diff --git a/model/cairoItems.cc b/model/cairoItems.cc index 8f422f757..2ce1ae618 100644 --- a/model/cairoItems.cc +++ b/model/cairoItems.cc @@ -96,9 +96,14 @@ bool RenderVariable::inImage(float x, float y) double RenderVariable::handlePos() const { - if (var.sliderStep::min() || std::isnan(var.sliderStep)) var.initSliderBounds(); // this should only be used when sliderStep's value has not been set or is a nonsensicl - var.adjustSliderBounds(); - return (w<0.5*var.iWidth()? 0.5*var.iWidth() : w)*(var.value()-0.5*(var.sliderMin+var.sliderMax))/(var.sliderMax-var.sliderMin); + if (auto vv=var.vValue()) + { + vv->adjustSliderBounds(); + assert(vv->sliderMinsliderMax); + return (w<0.5*var.iWidth()? 0.5*var.iWidth() : w)*(vv->value()-0.5*(vv->sliderMin+vv->sliderMax))/(vv->sliderMax-vv->sliderMin); + } + else + return 0; } void minsky::drawTriangle diff --git a/model/minsky.cc b/model/minsky.cc index b82658ac1..e45c06f3e 100644 --- a/model/minsky.cc +++ b/model/minsky.cc @@ -909,12 +909,7 @@ namespace minsky r->populateHypercube(vv->hypercube()); } else if (auto v=(*i)->variableCast()) - { //determine whether a slider should be shown - if (auto vv=v->vValue()) - vv->sliderVisible = v->enableSlider && - (v->type()==VariableType::parameter || (v->type()==VariableType::flow && !inputWired(v->valueId()))); - v->resetMiniPlot(); - } + v->resetMiniPlot(); return false; }); diff --git a/model/variable.cc b/model/variable.cc index 9dfd00cd8..367a85ff3 100644 --- a/model/variable.cc +++ b/model/variable.cc @@ -544,64 +544,16 @@ bool VariableBase::visible() const bool VariableBase::sliderVisible() const { auto vv=vValue(); - return enableSlider && + return enableSlider() && ((!vv && type()==parameter) || (vv && vv->size()==1 && - (type()==parameter || vv->sliderVisible))); + (type()==parameter || vv->enableSlider))); } - -void VariableBase::sliderSet(double x) -{ - if (xsliderMax) x=sliderMax; - sliderStep=maxSliderSteps(); - init(to_string(x)); - value(x); -} - - -void VariableBase::initSliderBounds() const -{ - if (!sliderBoundsSet) - { - if (value()==0) - { - sliderMin=-1; - sliderMax=1; - } - else - { - sliderMin=-value()*10; - sliderMax=value()*10; - } - sliderStepRel=false; - sliderBoundsSet=true; - sliderStep=0.01*(sliderMax-sliderMin); - } - sliderStep=maxSliderSteps(); -} - -void VariableBase::adjustSliderBounds() const +void VariableBase::adjustSliderBounds() { if (auto vv=vValue()) - // For feature 47 - if (vv->size()==1 && !isnan(vv->value())) // make sure sliderBoundsSet is defined. for tickets 1258/1263 - { - if (sliderMaxvalue()) - sliderMax=vv->value()? 10*vv->value():1; - if (sliderMin>vv->value()) - sliderMin=vv->value()? -10*vv->value():-1; - sliderStep=maxSliderSteps(); - sliderBoundsSet=true; - } -} - -double VariableBase::maxSliderSteps() const -{ - // ensure there are at most 10000 steps between sliderMin and Max. for ticket 1255. - if ((sliderMax-sliderMin)/sliderStep > 1.0e04) return (sliderMax-sliderMin)/1.0e04; - return sliderStep; + vv->adjustSliderBounds(); } bool VariableBase::onKeyPress(int keySym, const std::string&,int) @@ -609,13 +561,13 @@ bool VariableBase::onKeyPress(int keySym, const std::string&,int) switch (keySym) { case 0xff52: case 0xff53: //Right, Up - sliderSet(value()+(sliderStepRel? value(): 1)*sliderStep); - if (!minsky().running) minsky().requestReset(); - return true; + if (auto vv=vValue()) vv->incrSlider(1); + if (!minsky().running) minsky().requestReset(); + return true; case 0xff51: case 0xff54: //Left, Down - sliderSet(value()-(sliderStepRel? value(): 1)*sliderStep); - if (!minsky().running) minsky().requestReset(); - return true; + if (auto vv=vValue()) vv->incrSlider(-1); + if (!minsky().running) minsky().requestReset(); + return true; default: return false; } @@ -658,11 +610,14 @@ void VariableBase::resetMiniPlot() bool VariableBase::onMouseMotion(float x, float y) { - const RenderVariable rv(*this); - const double rw=fabs(zoomFactor()*(rv.width()x())* (sliderMax-sliderMin)/rw+0.5*(sliderMin+sliderMax); - const double sliderHatch=sliderPos-fmod(sliderPos,sliderStep); // matches slider's hatch marks to sliderStep value. for ticket 1258 - sliderSet(sliderHatch); + if (auto vv=vValue()) + { + const RenderVariable rv(*this); + const double rw=fabs(zoomFactor()*(rv.width()x())* (vv->sliderMax-vv->sliderMin)/rw+0.5*(vv->sliderMin+vv->sliderMax); + const double sliderHatch=sliderPos-fmod(sliderPos,vv->sliderStep); // matches slider's hatch marks to sliderStep value. for ticket 1258 + vv->sliderSet(sliderHatch); + } // push History to prevent an unnecessary reset when // adjusting the slider whilst paused. See ticket #812 minsky().pushHistory(); @@ -672,6 +627,76 @@ bool VariableBase::onMouseMotion(float x, float y) return true; } +double VariableBase::sliderMin() const +{ + if (auto vv=vValue()) + return vv->sliderMin; + return 0; +} + + +double VariableBase::sliderMin(double x) const +{ + if (auto vv=vValue()) + return vv->sliderMin=x; + return 0; +} + +double VariableBase::sliderMax() const +{ + if (auto vv=vValue()) + return vv->sliderMax; + return 0; +} + +double VariableBase::sliderMax(double x) const +{ + if (auto vv=vValue()) + return vv->sliderMax=x; + return 0; +} + +double VariableBase::sliderStep() const +{ + if (auto vv=vValue()) + return vv->sliderStep; + return 0; +} + +double VariableBase::sliderStep(double x) const +{ + if (auto vv=vValue()) + return vv->sliderStep=x; + return 0; +} + +bool VariableBase::sliderStepRel() const +{ + if (auto vv=vValue()) + return vv->sliderStepRel; + return false; +} + +bool VariableBase::sliderStepRel(bool x) const + { + if (auto vv=vValue()) + return vv->sliderStepRel=x; + return false; +} + +bool VariableBase::enableSlider() const +{ + if (auto vv=vValue()) + return vv->enableSlider; + return false; +} + +bool VariableBase::enableSlider(bool x) +{ + if (auto vv=vValue()) + return vv->enableSlider=x; + return false; +} void VariableBase::draw(cairo_t *cairo) const @@ -748,13 +773,13 @@ void VariableBase::draw(cairo_t *cairo) const { cachedValue=value(); if (!isnan(value())) { - if (sliderBoundsSet && vv->sliderVisible) + if (sliderVisible()) l_cachedMantissa->setMarkup (mantissa(val, int(1+ - (sliderStepRel? - -log10(maxSliderSteps()): - log10(value()/maxSliderSteps()) + (vv->sliderStepRel? + -log10(vv->maxSliderSteps()): + log10(vv->value()/vv->maxSliderSteps()) )))); else l_cachedMantissa->setMarkup(mantissa(val)); diff --git a/model/variable.h b/model/variable.h index 44ae560a1..5e04689ad 100644 --- a/model/variable.h +++ b/model/variable.h @@ -86,7 +86,7 @@ namespace minsky class VariableBase: virtual public classdesc::PolyPackBase, public BottomRightResizerItem, - public Slider, public VariableType, + public VariableType, public VarAccessors::NameAccessor, public VarAccessors::InitAccessor, public VarAccessors::ValueAccessor, @@ -190,14 +190,6 @@ namespace minsky /// returns true if slider is to be drawn bool sliderVisible() const; - /// sets variable value (or init value) - void sliderSet(double x); - /// initialise slider bounds when slider first opened - void initSliderBounds() const; - void adjustSliderBounds() const; - /// a maximum of at most 10000 slider steps permitted - double maxSliderSteps() const; - /// sets/gets the units associated with this type Units units(bool check) const override; Units units() const {return units(false);} @@ -251,7 +243,21 @@ namespace minsky void resetMiniPlot(); bool onMouseMotion(float x, float y) override; - + + void adjustSliderBounds(); + + /// @{ slider parameters + double sliderMin() const; + double sliderMin(double) const; + double sliderMax() const; + double sliderMax(double) const; + double sliderStep() const; + double sliderStep(double) const; + bool sliderStepRel() const; + bool sliderStepRel(bool) const; + bool enableSlider() const; + bool enableSlider(bool); + /// @} }; template diff --git a/model/variablePane.cc b/model/variablePane.cc index 39768e529..076e5c94c 100644 --- a/model/variablePane.cc +++ b/model/variablePane.cc @@ -47,7 +47,6 @@ namespace minsky void VariablePaneCell::show() { if (!cachedCairo || !var || var->type()==VariableType::undefined) return; - var->sliderBoundsSet=0; //TODO, this should be the case by default anyway RenderVariable rv(*var, cachedCairo); const ecolab::cairo::CairoSave cs(cachedCairo); cairo_translate(cachedCairo,0.5*m_width,0.5*m_height); diff --git a/schema/schema3.cc b/schema/schema3.cc index 024191952..824b4342b 100644 --- a/schema/schema3.cc +++ b/schema/schema3.cc @@ -526,12 +526,11 @@ namespace schema3 x1->name(*y.name); if (y.slider) { - x1->sliderBoundsSet=true; - x1->sliderStepRel=y.slider->stepRel; - x1->enableSlider=y.slider->visible; - x1->sliderMin=y.slider->min; - x1->sliderMax=y.slider->max; - x1->sliderStep=y.slider->step; + x1->sliderStepRel(y.slider->stepRel); + x1->enableSlider(y.slider->visible); + x1->sliderMin(y.slider->min); + x1->sliderMax(y.slider->max); + x1->sliderStep(y.slider->step); } x1->miniPlotEnabled(y.miniPlot); // variableValue attributes populated later once variable is homed in its group diff --git a/schema/schema3.h b/schema/schema3.h index e055c651e..979dfe4b1 100644 --- a/schema/schema3.h +++ b/schema/schema3.h @@ -143,8 +143,7 @@ namespace schema3 ItemBase(id,static_cast(v),ports), init(v.init()), miniPlot(v.miniPlotEnabled()) { name=v.rawName(); - if (v.sliderBoundsSet) - slider.reset(new Slider(v.sliderStepRel,v.enableSlider,v.sliderMin,v.sliderMax,v.sliderStep)); + slider.reset(new Slider(v.sliderStepRel(),v.enableSlider(),v.sliderMin(),v.sliderMax(),v.sliderStep())); if (auto vv=v.vValue()) { units=vv->units.str(); diff --git a/test/00/RESTService.sh b/test/00/RESTService.sh index d45482678..d08351213 100755 --- a/test/00/RESTService.sh +++ b/test/00/RESTService.sh @@ -9,7 +9,7 @@ EOF if [ $? -ne 0 ]; then fail; fi cat >reference <{"csvDialog":{"backgroundColour":{"a":1,"b":0.80000000000000004,"g":0.80000000000000004,"r":0.80000000000000004},"colWidth":50,"flashNameRow":false,"item":{},"resolutionScaleFactor":1,"spec":{"counter":false,"dataColOffset":0,"dataCols":[],"dataRowOffset":0,"decSeparator":".","dimensionCols":[],"dimensionNames":[],"dimensions":[],"dontFail":false,"duplicateKeyAction":"throwException","escape":"\u0000","headerRow":0,"horizontalDimName":"?","horizontalDimension":{"type":"string","units":""},"maxColumn":1000,"mergeDelimiters":false,"missingValue":NaN,"numCols":0,"quote":"\\"","separator":","},"url":"","wire":{},"xoffs":80},"detailedText":"","godleyOverridden":false,"name":"constant:one","rhs":{},"sliderVisible":false,"tensorInit":{},"tooltip":"","units":[],"unitsCached":false} +/minsky/variableValues/@elem/"constant:one"=>{"csvDialog":{"backgroundColour":{"a":1,"b":0.80000000000000004,"g":0.80000000000000004,"r":0.80000000000000004},"colWidth":50,"flashNameRow":false,"item":{},"resolutionScaleFactor":1,"spec":{"counter":false,"dataColOffset":0,"dataCols":[],"dataRowOffset":0,"decSeparator":".","dimensionCols":[],"dimensionNames":[],"dimensions":[],"dontFail":false,"duplicateKeyAction":"throwException","escape":"\u0000","headerRow":0,"horizontalDimName":"?","horizontalDimension":{"type":"string","units":""},"maxColumn":1000,"mergeDelimiters":false,"missingValue":NaN,"numCols":0,"quote":"\"","separator":","},"url":"","wire":{},"xoffs":80},"detailedText":"","enableSlider":true,"godleyOverridden":false,"name":"constant:one","rhs":{},"sliderMax":-1.7976931348623157e+308,"sliderMin":1.7976931348623157e+308,"sliderStep":0,"sliderStepRel":false,"tensorInit":{},"tooltip":"","units":[],"unitsCached":false} EOF diff -q -w output reference diff --git a/test/00/editItems.sh b/test/00/editItems.sh deleted file mode 100755 index f3a4a4f68..000000000 --- a/test/00/editItems.sh +++ /dev/null @@ -1,95 +0,0 @@ -#! /bin/sh - -here=`pwd` -if test $? -ne 0; then exit 2; fi -tmp=/tmp/$$ -mkdir $tmp -if test $? -ne 0; then exit 2; fi -cd $tmp -if test $? -ne 0; then exit 2; fi - -fail() -{ - echo "FAILED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 1 -} - -pass() -{ - echo "PASSED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 0 -} - -trap "fail" 1 2 3 15 -cat >input.tcl <=0} "plot" - - minsky.addGodley - assert {[findObject GodleyIcon]} - assert {[lsearch -glob [winfo children .] .godley*]==-1} "godley" - editItem - assert {[lsearch -glob [winfo children .] .godley*]>=0} "godley" - - minsky.addGroup - assert {[findObject Group]} - editItem - assert {[winfo ismapped .wiring.editGroup]} "group" - destroy .wiring.editGroup - - minsky.addNote "hello" - assert {[findObject Item]} - editItem - assert {[winfo ismapped .wiring.note]} "note" - destroy .wiring.note - grab release .wiring.note - - minsky.addSwitch - editItem -# should be do nothing ATM - - tcl_exit -} -EOF - -$here/gui-tk/minsky input.tcl -if [ $? -ne 0 ]; then fail; fi - -pass diff --git a/test/00/t0030a.sh b/test/00/t0030a.sh deleted file mode 100755 index b9ba24e58..000000000 --- a/test/00/t0030a.sh +++ /dev/null @@ -1,68 +0,0 @@ -#! /bin/sh - -here=`pwd` -if test $? -ne 0; then exit 2; fi -tmp=/tmp/$$ -mkdir $tmp -if test $? -ne 0; then exit 2; fi -cd $tmp -if test $? -ne 0; then exit 2; fi - -fail() -{ - echo "FAILED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 1 -} - -pass() -{ - echo "PASSED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 0 -} - -trap "fail" 1 2 3 15 - -# insert ecolab script code here -# use \$ in place of $ to refer to variable contents -# exit 0 to indicate pass, and exit 1 to indicate failure -cat >input.tcl <=0} - - # now double click on one of the flow variables - findVariable IntLoan - set var minsky.canvas.item - doubleButton [\$var.x] [\$var.y] - assert {[winfo viewable .wiring.editVar]} - .wiring.editVar.buttonBar.ok invoke - assert {![winfo exists .wiring.editVar]} - - assert {[findVariable Loan]} - editItem - assert {[winfo viewable .wiring.editVar]} {varclick} - .wiring.editVar.buttonBar.ok invoke - assert {![winfo exists .wiring.editVar]} {varclick} - - - tcl_exit -} -EOF - -$here/gui-tk/minsky input.tcl -if test $? -ne 0; then fail; fi - -pass diff --git a/test/00/textInput.sh b/test/00/textInput.sh deleted file mode 100755 index b63dae462..000000000 --- a/test/00/textInput.sh +++ /dev/null @@ -1,98 +0,0 @@ -#! /bin/sh - -here=`pwd` -if test $? -ne 0; then exit 2; fi -tmp=/tmp/$$ -mkdir $tmp -if test $? -ne 0; then exit 2; fi -cd $tmp -if test $? -ne 0; then exit 2; fi - -fail() -{ - echo "FAILED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 1 -} - -pass() -{ - echo "PASSED" 1>&2 - cd $here - chmod -R u+w $tmp - rm -rf $tmp - exit 0 -} - -trap "fail" 1 2 3 15 - -# exercise item creation -cat >input.tcl < -x 100 -y 100 -assert {[model.items.size]==1} - -addConstant -.wiring.initVar.buttonBar.ok invoke -event generate .wiring.canvas -x 100 -y 100 -assert {[model.items.size]==2} - -addOperationKey add -assert {[model.items.size]==3} - -textInput a -assert {[winfo exists .textInput]} -.textInput.buttonBar.ok invoke -assert {[winfo exists .wiring.editVar]} -.wiring.editVar.buttonBar.ok invoke -assert {[model.items.size]==4} - -textInput f -assert {[winfo exists .textInput]} -.textInput.entry insert 1 "=0.2" -.textInput.buttonBar.ok invoke -assert {![winfo exists .wiring.editVar]} -assert {[model.items.size]==5} -assert {[findVariable f]} {} -assert {[minsky.canvas.item.init]=="0.2"} - -textInput % -.textInput.entry insert 1 "b" -.textInput.buttonBar.ok invoke -assert {[model.items.size]==6} -assert {[findObject Item]} {} -# TODO reenable in python version -#assert {[minsky.canvas.item.detailedText]=="b"} - -textInput s -.textInput.entry insert 1 "in" -.textInput.buttonBar.ok invoke -assert {[model.items.size]==7} -assert {[findObject "Operation:sin"]} - -addGodley -event generate .wiring.canvas -assert {[model.items.size]==8} - -addPlot -event generate .wiring.canvas -assert {[model.items.size]==9} - - -tcl_exit -}} -EOF - -cp $here/test/assert.tcl . -$here/gui-tk/minsky input.tcl -if test $? -ne 0; then fail; fi - -pass diff --git a/test/testModel.cc b/test/testModel.cc index 8e19e2301..8caeb7f14 100644 --- a/test/testModel.cc +++ b/test/testModel.cc @@ -499,9 +499,9 @@ SUITE(Canvas) cc->moveTo(500,300); auto cv=dynamic_cast(cc.get()); cv->value(1000); - cv->sliderMin=0; - cv->sliderMax=2000; - cv->sliderStep=20; + cv->sliderMin(0); + cv->sliderMax(2000); + cv->sliderStep(20); // work out where slider is located RenderVariable rv(*cv); float xc=cv->x()+rv.handlePos(), yc=cv->y()-rv.height(); @@ -519,11 +519,11 @@ SUITE(Canvas) // now check that value is clamped to max/min canvas.mouseDown(xc,yc); canvas.mouseUp(xc+100,yc); - CHECK_EQUAL(cv->sliderMax, cv->value()); + CHECK_EQUAL(cv->sliderMax(), cv->value()); xc=cv->x()+rv.handlePos(); canvas.mouseDown(xc,yc); canvas.mouseUp(xc-100,yc); - CHECK_EQUAL(cv->sliderMin, cv->value()); + CHECK_EQUAL(cv->sliderMin(), cv->value()); } TEST_FIXTURE(TestFixture,lasso) @@ -919,9 +919,9 @@ SUITE(Canvas) auto v=model->addItem(new Variable("v"))->variableCast(); v->value(1000); - v->sliderMin=0; - v->sliderMax=2000; - v->sliderStep=100; + v->sliderMin(0); + v->sliderMax(2000); + v->sliderStep(100); canvas.keyPress({0xff52,"",0,v->x(),v->y()}); CHECK_EQUAL(1100,v->value()); canvas.keyPress({0xff51,"",0,v->x(),v->y()}); diff --git a/test/testPubTab.cc b/test/testPubTab.cc index ea15b97aa..26da86e5a 100644 --- a/test/testPubTab.cc +++ b/test/testPubTab.cc @@ -133,10 +133,9 @@ SUITE(PubTab) { VariablePtr var(VariableBase::parameter, "foobar"); // this member should always be initialised, but occasionally is not, causing the test to fail. Why? - var->sliderBoundsSet=false; - var->initSliderBounds(); - model->addItem(var); var->value(0); + var->adjustSliderBounds(); + model->addItem(var); var->updateBoundingBox(); auto& tab=publicationTabs[0]; tab.items.emplace_back(var); @@ -145,7 +144,7 @@ SUITE(PubTab) CHECK(var->clickType(x-item.x,y-item.y)==ClickType::inItem); tab.mouseDown(x,y); tab.mouseUp(x+0.5*var->width(),y); - CHECK_CLOSE(var->sliderMax,var->value(),0.1); + CHECK_CLOSE(var->sliderMax(),var->value(),0.1); } TEST_FIXTURE(MinskyFixture,redraw) {