public Customize_ScoringRules_Layout(UserPreferences_Database userPreferencesDatabase, LayoutStack layoutStack) { this.userPreferencesDatabase = userPreferencesDatabase; this.layoutStack = layoutStack; this.viewLayout = new View_ScoringRules_Layout(userPreferencesDatabase); this.newRule_layout = new New_ScoringRule_Layout(userPreferencesDatabase); BoundProperty_List rowHeights = new BoundProperty_List(2); rowHeights.BindIndices(0, 1); rowHeights.SetPropertyScale(0, 7); rowHeights.SetPropertyScale(1, 1); GridLayout grid = GridLayout.New(rowHeights, new BoundProperty_List(1), LayoutScore.Zero); grid.AddLayout(this.viewLayout); Button newRuleButton = new Button(); newRuleButton.Clicked += NewRuleButton_Clicked; grid.AddLayout(new ButtonLayout(newRuleButton, "New Rule")); this.SubLayout = ScrollLayout.New(grid); }
private void showResults(Activities_HappinessContributions contributions, DateTime start, DateTime end) { // title string title = "" + (contributions.Best.Count + contributions.Worst.Count) + " Activities adding or subtracting the most happiness from " + start + " to " + end; BoundProperty_List heights = new BoundProperty_List(4); heights.BindIndices(1, 3); GridLayout grid = GridLayout.New(heights, new BoundProperty_List(1), LayoutScore.Zero); grid.AddLayout(new TextblockLayout(title)); // contents GridLayout_Builder topBuilder = new Vertical_GridLayout_Builder().Uniform(); // Show the top activities from best to worst for (int i = 0; i < contributions.Best.Count; i++) { ActivityHappinessContribution item = contributions.Best[i]; topBuilder.AddLayout(this.renderContribution("top " + (i + 1) + ": ", item, start)); } grid.AddLayout(ScrollLayout.New(topBuilder.BuildAnyLayout())); // Use the help button as the divider between the best and worst activities LayoutChoice_Set helpWindow = new HelpWindowBuilder() .AddMessage("Activities that you like more than average are accompanied by positive numbers.") .AddMessage("Activities that you like less than average are accompanied by negative numbers.") .AddMessage("Activities that you participated in for more total time are accompanied by numbers that are further from 0.") .AddMessage("Specifically, each of these numbers is calculated by looking at all of the participations in that activity during this time, " + "computing the difference between the happiness of that participation and your overall average happiness, " + "multiplying each by the duration of its participation, and adding up the results.") .Build(); grid.AddLayout(new HelpButtonLayout("?", helpWindow, this.layoutStack)); GridLayout_Builder bottomBuilder = new Vertical_GridLayout_Builder().Uniform(); // Show the bottom activities, also from best to worst for (int i = contributions.Worst.Count - 1; i >= 0; i--) { bottomBuilder.AddLayout(this.renderContribution("bottom " + (i + 1) + ": ", contributions.Worst[i], start)); } grid.AddLayout(bottomBuilder.BuildAnyLayout()); this.layoutStack.AddLayout(grid, "Significant Activities"); }
private void setupDrawing() { this.textA = new Editor(); this.textA.Text = this.protoA.Text; this.textB = new Editor(); this.textB.Text = this.protoB.Text; TextblockLayout description = new TextblockLayout("Split into two protoactivities, then go back"); BoundProperty_List rowHeights = new BoundProperty_List(3); rowHeights.BindIndices(0, 2); GridLayout grid = GridLayout.New(rowHeights, new BoundProperty_List(1), LayoutScore.Zero); grid.AddLayout(ScrollLayout.New(new TextboxLayout(this.textA))); grid.AddLayout(description); grid.AddLayout(ScrollLayout.New(new TextboxLayout(this.textB))); this.SubLayout = grid; }
public ExperimentInitializationLayout(LayoutStack layoutStack, ActivityRecommender activityRecommender, ActivityDatabase activityDatabase, ProtoActivity_Database protoActivity_database, Engine engine, int numActivitiesThatMayBeRequestedDirectly) { this.SetTitle("Efficiency Experiment"); this.activityRecommender = activityRecommender; Button okbutton = new Button(); this.okButtonLayout = new ButtonLayout(okbutton, "Next"); okbutton.Clicked += Okbutton_Clicked; LayoutChoice_Set helpButton = this.make_helpButton(layoutStack); SuggestedMetric_Metadata experimentsStatus = activityRecommender.Test_ChooseExperimentOption(); if (experimentsStatus.Error != "") { this.SetContent(new TextblockLayout(experimentsStatus.Error)); return; } this.statusHolder = new ContainerLayout(); GridLayout topGrid = new Horizontal_GridLayout_Builder() .AddLayout(helpButton) .AddLayout(new HelpButtonLayout("Browse Activities", new ActivitySearchView(activityDatabase, protoActivity_database, layoutStack), layoutStack)) .Uniform() .Build(); GridLayout_Builder childrenBuilder = new Horizontal_GridLayout_Builder().Uniform(); for (int i = 0; i < this.numChoices; i++) { bool allowRequestingActivityDirectly = (i < numActivitiesThatMayBeRequestedDirectly); ExperimentOptionLayout child = new ExperimentOptionLayout(this, activityDatabase, allowRequestingActivityDirectly, engine, layoutStack); this.children.Add(child); childrenBuilder.AddLayout(child); child.SuggestionDismissed += Child_SuggestionDismissed; child.JustifySuggestion += Child_JustifySuggestion; } GridLayout bottomGrid = childrenBuilder.Build(); BoundProperty_List rowHeights = new BoundProperty_List(3); rowHeights.BindIndices(0, 1); rowHeights.BindIndices(0, 2); rowHeights.SetPropertyScale(0, 2); rowHeights.SetPropertyScale(1, 1); rowHeights.SetPropertyScale(2, 6); GridLayout mainGrid = GridLayout.New(rowHeights, new BoundProperty_List(1), LayoutScore.Zero); mainGrid.AddLayout(topGrid); mainGrid.AddLayout(this.statusHolder); mainGrid.AddLayout(bottomGrid); string statusMessage = "(" + experimentsStatus.NumExperimentParticipationsRemaining + " experiment"; if (experimentsStatus.NumExperimentParticipationsRemaining != 1) { statusMessage += "s"; } statusMessage += " remaining before another ToDo must be entered!)"; this.UpdateStatus(statusMessage); this.SetContent(mainGrid); }
public BrowseBest_ProtoActivities_Layout(ProtoActivity_Database protoActivity_database, ActivityDatabase activityDatabase, LayoutStack layoutStack) { this.protoActivity_database = protoActivity_database; this.layoutStack = layoutStack; this.activityDatabase = activityDatabase; Button edit1Button = new Button(); edit1Button.Text = "Edit"; edit1Button.Clicked += Edit1Button_Clicked; Button edit2Button = new Button(); edit2Button.Text = "Edit"; edit2Button.Clicked += Edit2Button_Clicked; Button mark1Worse_button = new Button(); mark1Worse_button.Text = "Worse"; mark1Worse_button.Clicked += Mark1Worse_button_Clicked; Button explainScore1Button = new Button(); explainScore1Button.Text = "?"; explainScore1Button.Clicked += ExplainScore1Button_Clicked; this.explainScore1Button = new ButtonLayout(explainScore1Button); Button mark2Worse_button = new Button(); mark2Worse_button.Text = "Worse"; mark2Worse_button.Clicked += Mark2Worse_button_Clicked; Button explainScore2Button = new Button(); explainScore2Button.Text = "?"; explainScore2Button.Clicked += ExplainScore2Button_Clicked; this.explainScore2Button = new ButtonLayout(explainScore2Button); BoundProperty_List rowHeights = BoundProperty_List.Uniform(3); BoundProperty_List columnWidths = new BoundProperty_List(3); columnWidths.BindIndices(0, 1); columnWidths.BindIndices(0, 2); columnWidths.SetPropertyScale(0, 1); columnWidths.SetPropertyScale(1, 4); columnWidths.SetPropertyScale(2, 1); GridLayout grid = GridLayout.New(rowHeights, columnWidths, LayoutScore.Zero); this.numBrowsesPerProtoactivity_Layout = new TextblockLayout().AlignVertically(TextAlignment.Center); grid.PutLayout(this.numBrowsesPerProtoactivity_Layout, 0, 0); this.titleLayout = new TextblockLayout("Browse Best ProtoActivities"); grid.PutLayout(this.titleLayout, 1, 0); LayoutChoice_Set helpButton = new HelpButtonLayout( new HelpWindowBuilder() .AddMessage("If you have entered any ProtoActivities (which are ideas that are not yet fully-formed enough for you to want them to be suggested), then you can browse them here.") .AddMessage("This screen allows you to see the ProtoActivities that ActivityRecommender thinks you consider to be most interesting, and also asks you to choose which one of the top " + "two is most interesting to you.") .AddMessage("If you want to modify a ProtoActivity, press its Edit button (note that if you make any changes, then this will temporarily dismiss it (by resetting its interest score)).") .AddMessage("If you want to see different ProtoActivities, then first you should choose which one (of the two visible ProtoActivities) you like less. Press the button marked " + "'Worse' next to the ProtoActivity that you like less. This will cause two new ProtoActivities to appear (by resetting the interest scores of the two currently visible " + "ProtoActivities to 0). This will also cause the one you marked 'Worse' to return less often and " + "the other one to return more often.") .AddMessage("The way that ProtoActivities are chosen in this screen is that each ProtoActivity has a score of how much you like it, and " + "a duration since the last time you interacted with that ProtoActivity. The product of the two is its interest score, and the " + "ProtoActivities with the highest interest scores are the ones that will be displayed.") .AddMessage("If you want to see the calculation of the interest scores of the two current ProtoActivities, press the \"?\" button.") .AddMessage("Enjoy!") .AddLayout(new CreditsButtonBuilder(layoutStack) .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 5, 28), "Suggested explaining the calculation of protoactivity sort score") .Build() ) .Build(), layoutStack); grid.PutLayout(helpButton, 2, 0); grid.PutLayout(new ButtonLayout(edit1Button), 0, 1); grid.PutLayout(new ButtonLayout(edit2Button), 0, 2); this.activity1Holder = new ContainerLayout(); grid.PutLayout(this.activity1Holder, 1, 1); this.activity2Holder = new ContainerLayout(); grid.PutLayout(this.activity2Holder, 1, 2); this.activity1ScoreBlock = new TextblockLayout(); this.score1Holder = new ContainerLayout(); this.score2Holder = new ContainerLayout(); grid.PutLayout( new Vertical_GridLayout_Builder() .Uniform() .AddLayout(new ButtonLayout(mark1Worse_button)) .AddLayout(this.score1Holder) .BuildAnyLayout() , 2, 1); this.activity2ScoreBlock = new TextblockLayout(); grid.PutLayout( new Vertical_GridLayout_Builder() .Uniform() .AddLayout(new ButtonLayout(mark2Worse_button)) .AddLayout(this.score2Holder) .BuildAnyLayout() , 2, 2); this.multiActivitiesLayout = grid; this.protoActivity_database.RatingsChanged += ProtoActivity_database_Changed; this.protoActivity_database.TextChanged += ProtoActivity_database_Changed; this.singleActivityButton = new Button(); this.singleActivityButton.Clicked += SingleActivityButton_Clicked; this.singleActivityLayout = new ButtonLayout(this.singleActivityButton); this.invalidate(); }
public ParticipationEntryView(ActivityDatabase activityDatabase, LayoutStack layoutStack) { this.activityDatabase = activityDatabase; activityDatabase.ActivityAdded += ActivityDatabase_ActivityAdded; this.layoutStack = layoutStack; BoundProperty_List rowHeights = new BoundProperty_List(6); rowHeights.BindIndices(0, 1); rowHeights.BindIndices(0, 2); rowHeights.BindIndices(0, 3); rowHeights.SetPropertyScale(0, 5); // activity name and feedback rowHeights.SetPropertyScale(1, 5); // rating, comments, and metrics rowHeights.SetPropertyScale(2, 2.3); // start and end times rowHeights.SetPropertyScale(3, 2); // buttons // activity name and feedback Vertical_GridLayout_Builder nameAndFeedback_builder = new Vertical_GridLayout_Builder(); GridLayout contents = GridLayout.New(rowHeights, BoundProperty_List.Uniform(1), LayoutScore.Zero); this.nameBox = new ActivityNameEntryBox("What Have You Been Doing?", activityDatabase, layoutStack); this.nameBox.AutoAcceptAutocomplete = false; this.nameBox.PreferSuggestibleActivities = true; this.nameBox.NameTextChanged += this.ActivityNameText_Changed; nameAndFeedback_builder.AddLayout(this.nameBox); this.promptHolder = new ContainerLayout(); nameAndFeedback_builder.AddLayout(this.promptHolder); Button responseButton = new Button(); responseButton.Clicked += ResponseButton_Clicked; this.participationFeedbackButtonLayout = new ButtonLayout(responseButton); contents.AddLayout(nameAndFeedback_builder.BuildAnyLayout()); Button acceptSuggestion_button = new Button(); acceptSuggestion_button.Clicked += AcceptSuggestions_button_Clicked; Button visitSuggestions_button = new Button(); visitSuggestions_button.Clicked += VisitSuggestions_button_Clicked; this.suggestionLayout = new TextblockLayout(); this.suggestionsLayout = new Vertical_GridLayout_Builder() .AddLayout(suggestionLayout) .AddLayout(new Horizontal_GridLayout_Builder().Uniform() .AddLayout(new ButtonLayout(acceptSuggestion_button, "Yes")) .AddLayout(new ButtonLayout(visitSuggestions_button, "More ideas")) .BuildAnyLayout() ) .BuildAnyLayout(); Button experimentFeedbackButton = new Button(); experimentFeedbackButton.Clicked += ExperimentFeedbackButton_Clicked; this.experimentFeedbackLayout = new ButtonLayout(experimentFeedbackButton, "Experiment Complete!"); Vertical_GridLayout_Builder detailsBuilder = new Vertical_GridLayout_Builder(); GridLayout commentAndRating_grid = GridLayout.New(BoundProperty_List.Uniform(1), BoundProperty_List.Uniform(2), LayoutScore.Zero); this.ratingBox = new RelativeRatingEntryView(); this.ratingBox.RatingRatioChanged += RatingBox_RatingRatioChanged; commentAndRating_grid.AddLayout(this.ratingBox); this.commentBox = new PopoutTextbox("Comment", layoutStack); this.commentBox.Placeholder("(Optional)"); commentAndRating_grid.AddLayout(this.commentBox); detailsBuilder.AddLayout(commentAndRating_grid); this.todoCompletionStatusHolder = new ContainerLayout(); this.metricChooser = new ChooseMetric_View(true); this.metricChooser.ChoseNewMetric += TodoCompletionLabel_ChoseNewMetric; LayoutChoice_Set metricLayout = new Vertical_GridLayout_Builder().Uniform() .AddLayout(this.metricChooser) .AddLayout(this.todoCompletionStatusHolder) .Build(); this.helpStatusHolder = new ContainerLayout(); GridLayout_Builder centered_todoInfo_builder = new Horizontal_GridLayout_Builder().Uniform(); centered_todoInfo_builder.AddLayout(metricLayout); centered_todoInfo_builder.AddLayout(this.helpStatusHolder); GridLayout_Builder offset_todoInfo_builder = new Horizontal_GridLayout_Builder(); offset_todoInfo_builder.AddLayout(metricLayout); offset_todoInfo_builder.AddLayout(this.helpStatusHolder); LayoutChoice_Set metricStatusLayout = new LayoutUnion( centered_todoInfo_builder.Build(), new ScoreShifted_Layout( offset_todoInfo_builder.Build(), LayoutScore.Get_UnCentered_LayoutScore(1) ) ); this.helpStatusPicker = new HelpDurationInput_Layout(this.layoutStack); detailsBuilder.AddLayout(metricStatusLayout); contents.AddLayout(detailsBuilder.BuildAnyLayout()); GridLayout grid3 = GridLayout.New(BoundProperty_List.Uniform(1), BoundProperty_List.Uniform(2), LayoutScore.Zero); this.startDateBox = new DateEntryView("Start Time", this.layoutStack); this.startDateBox.Add_TextChanged_Handler(new EventHandler <TextChangedEventArgs>(this.DateText_Changed)); grid3.AddLayout(this.startDateBox); this.endDateBox = new DateEntryView("End Time", this.layoutStack); this.endDateBox.Add_TextChanged_Handler(new EventHandler <TextChangedEventArgs>(this.DateText_Changed)); grid3.AddLayout(this.endDateBox); contents.AddLayout(grid3); this.setStartdateButton = new Button(); this.setEnddateButton = new Button(); this.okButton = new Button(); LayoutChoice_Set helpWindow = (new HelpWindowBuilder()).AddMessage("Use this screen to record participations.") .AddMessage("1. Type the name of the activity that you participated in, and press Enter if you want to take the autocomplete suggestion.") .AddMessage("You must have entered some activities in the activity name entry screen in order to enter them here.") .AddMessage("Notice that once you enter an activity name, ActivityRecommender will tell you how it estimates this will affect your longterm happiness.") .AddMessage("2. You may enter a rating (this is strongly recommended). The rating is a measurement of how much happiness you received per unit time from " + "this participation divided by the amount of happiness you received per unit time for the previous. " + "(The ratio that you enter will be combined with ActivityRecommender's previous expectations of how much you would enjoy these two " + "participations, and will be used to create an appropriate absolute rating from 0 to 1 for this participation.)") .AddMessage("If this Activity is a ToDo, you will see a box asking you to specify whether you completed the ToDo. Press the box if you completed it.") .AddMessage("3. Enter a start date and an end date. If you use the \"End = Now\" button right when the activity completes, you don't even need to type the date in. If you " + "do have to type the date in, press the white box.") .AddMessage("4. Enter a comment if you like.") .AddMessage("5. Lastly, press OK.") .AddMessage("It's up to you how many participations you log, how often you rate them, and how accurate the start and end dates are. ActivityRecommender will be able to " + "provide more useful help to you if you provide more accurate data, but even just a few participations per day should still be enough for meaningful feedback.") .AddLayout(new CreditsButtonBuilder(layoutStack) .AddContribution(ActRecContributor.AARON_SMITH, new DateTime(2019, 8, 17), "Pointed out out that it was hard to tell when the participation and suggestion screens are not yet relevant due to not having any activities") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2019, 11, 10), "Suggested disallowing entering participations having empty durations") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2019, 11, 28), "Mentioned that the keyboard was often in the way of text boxes on iOS") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 1, 26), "Pointed out that feedback should be relative to average rather happiness than relative to the previous participation") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 4, 19), "Discussed participation feedback messages") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 7, 12), "Pointed out that the time required to log a participation can cause the end time of the next participation to be a couple minutes after the previous one") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 8, 15), "Suggested that if the participation feedback recommends a different time, then it should specify which time") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 8, 30), "Pointed out that participation feedback was missing more often than it should have been.") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 8, 30), "Pointed out that the text in the starttime box had stopped fitting properly.") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 10, 3), "Pointed out that it was possible record participations in the future.") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 3, 9), "Suggested making different metrics appear more distinct.") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 3, 21), "Pointed out that the participation feedback had stopped finding a better activity") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 6, 6), "Suggested showing suggestions in the participation entry view") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 7, 2), "Suggested shortening the text in the rating entry view") .Build() ) .Build(); GridLayout grid4 = GridLayout.New(BoundProperty_List.Uniform(1), BoundProperty_List.Uniform(4), LayoutScore.Zero); grid4.AddLayout(new ButtonLayout(this.setStartdateButton, "Start = now", 16)); grid4.AddLayout(new ButtonLayout(this.okButton)); grid4.AddLayout(new HelpButtonLayout(helpWindow, this.layoutStack)); grid4.AddLayout(new ButtonLayout(this.setEnddateButton, "End = now", 16)); contents.AddLayout(grid4); this.mainLayout = LayoutCache.For(contents); Vertical_GridLayout_Builder noActivities_help_builder = new Vertical_GridLayout_Builder(); noActivities_help_builder.AddLayout(new TextblockLayout("This screen is where you will be able to record having participated in an activity.\n")); noActivities_help_builder.AddLayout( new HelpButtonLayout("Recording a participation is deceptively easy", new HelpWindowBuilder() .AddMessage("Autocomplete is everywhere in ActivityRecommender and is very fast. You will be impressed.") .AddMessage("Autocomplete is one of the reasons that you must enter an Activity before you can record a participation, so " + "ActivityRecommender can know which activity you're referring to, usually after you type only one or two letters.") .Build() , layoutStack ) ); noActivities_help_builder.AddLayout( new HelpButtonLayout("You get feedback!", new HelpWindowBuilder() .AddMessage("Nearly every time you record a participation, ActivityRecommender will give you feedback on what you're doing. " + "This feedback will eventually contain suggestions of other things you could be doing now, and alternate times for what you " + "did do. This feedback gets increasingly specific and increasingly accurate as you record more data, eventually including " + "current happiness, future happiness, and future efficiency. Wow!") .Build() , layoutStack ) ); noActivities_help_builder.AddLayout(new TextblockLayout("Before you can record a participation, ActivityRecommender needs you to go back " + "and add some activities first. Here is a convenient button for jumping directly to the Activities screen:")); Button activitiesButton = new Button(); activitiesButton.Text = "Activities"; activitiesButton.Clicked += ActivitiesButton_Clicked; noActivities_help_builder.AddLayout(new ButtonLayout(activitiesButton)); this.noActivities_explanationLayout = noActivities_help_builder.BuildAnyLayout(); }
private void HelpButton_Clicked(object sender, EventArgs e) { this.statsLayoutHolder.SubLayout = new TextblockLayout(""); Button ratingsToggle = new Button(); ratingsToggle.Clicked += RatingsButton_Clicked; Button ratingsTrendToggle = new Button(); ratingsTrendToggle.Clicked += RatingsTrendButton_Clicked; Button overallHappinessToggle = new Button(); overallHappinessToggle.Clicked += OverallHappinessToggle_Clicked; Button efficiencyToggle = new Button(); efficiencyToggle.Clicked += EfficiencyToggle_Clicked; Button efficiencyTrendToggle = new Button(); efficiencyTrendToggle.Clicked += EfficiencyTrendToggle_Clicked; LayoutChoice_Set ratingPlotsLayout = new Vertical_GridLayout_Builder().Uniform() .AddLayout(new ButtonLayout(ratingsToggle, "Green: ratings")) .AddLayout(new ButtonLayout(ratingsTrendToggle, "Red: trend of ratings")) .AddLayout(new ButtonLayout(overallHappinessToggle, "Blue: overall happiness (all activities)")) .AddLayout(new ButtonLayout(efficiencyToggle, "Yellow: efficiency (all activities)")) .AddLayout(new ButtonLayout(efficiencyTrendToggle, "White: trend of efficiency")) .AddLayout(new TextblockLayout("Tick marks: months, days or years")) .Build(); Button timeToggle = new Button(); timeToggle.Clicked += TimeToggle_Clicked; Button timeTrendToggle = new Button(); timeTrendToggle.Clicked += TimeTrendToggle_Clicked; Button numSuggestionsToggle = new Button(); numSuggestionsToggle.Clicked += NumSuggestionsToggle_Clicked; Button effectivenessToggle = new Button(); effectivenessToggle.Clicked += EffectivenessToggle_Clicked; LayoutChoice_Set participationPlotsLayout = new Vertical_GridLayout_Builder().Uniform() .AddLayout(new ButtonLayout(timeToggle, "Green: cumulative time spent")) .AddLayout(new ButtonLayout(timeTrendToggle, "Red: trend of time spent")) .AddLayout(new ButtonLayout(numSuggestionsToggle, "Blue: cumulative # suggestions")) .AddLayout(new ButtonLayout(effectivenessToggle, "Yellow: cumulative effectiveness (efficiency * time)")) .AddLayout(new TextblockLayout("Tick marks: months, days or years")) .Build(); LayoutChoice_Set creditsLayout = new CreditsButtonBuilder(this.layoutStack) .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 3, 28), "Pointed out that the top of the app was occluded on iOS") .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 1, 24), "Asked for the graph legends to appear at the same time as the graphs") .Build(); BoundProperty_List rowHeights = new BoundProperty_List(3); rowHeights.BindIndices(0, 1); GridLayout helpGrid = GridLayout.New(rowHeights, new BoundProperty_List(1), LayoutScore.Zero); helpGrid.AddLayout(ratingPlotsLayout); helpGrid.AddLayout(participationPlotsLayout); helpGrid.AddLayout(creditsLayout); this.statsLayoutHolder.SubLayout = helpGrid; }