Пример #1
0
        public override SpecificLayout GetBestLayout(LayoutQuery query)
        {
            if (this.text == null)
            {
                if (this.textProvider == null)
                {
                    this.text = "Reading logs on this platform is not supported. Sorry";
                }
                else
                {
                    this.text = textProvider.Get().ReadToEnd();
                }

                Vertical_GridLayout_Builder builder = new Vertical_GridLayout_Builder();
                // Split text into multiple text blocks so that if roudning error occurs in text measurement, the errors are spaced evenly.
                // There could be 50 pages of text, and we don't want 2% rounding error to add a completely blank page at the end
                List <string> lines            = new List <string>(this.text.Split(new char[] { '\n' }));
                int           numLinesPerBlock = 10;
                for (int i = 0; i < lines.Count; i += numLinesPerBlock)
                {
                    int           maxIndex     = Math.Min(i + numLinesPerBlock, lines.Count);
                    List <string> currentLines = lines.GetRange(i, maxIndex - i);
                    string        currentText  = string.Join("\n", currentLines);
                    builder.AddLayout(new TextblockLayout(currentText, 16, false, true));
                }

                this.SubLayout = ScrollLayout.New(builder.BuildAnyLayout());
            }
            return(base.GetBestLayout(query));
        }
Пример #2
0
 private void update()
 {
     if (this.protoActivity_database.ProtoActivities.Count() < 1)
     {
         this.SubLayout = new TextblockLayout("No protoactivities!");
     }
     else
     {
         GridLayout_Builder   builder         = new Vertical_GridLayout_Builder().Uniform();
         List <ProtoActivity> protoActivities = new List <ProtoActivity>(protoActivity_database.ProtoActivities);
         protoActivities.Reverse();
         if (this.startIndex < 0)
         {
             this.startIndex = 0;
         }
         if (startIndex >= protoActivities.Count)
         {
             startIndex = protoActivities.Count - 1;
         }
         int endIndex = Math.Min(startIndex + this.pageCount, protoActivities.Count);
         if (this.startIndex > 0)
         {
             builder.AddLayout(this.prevButtonLayout);
         }
         for (int i = startIndex; i < endIndex; i++)
         {
             builder.AddLayout(this.summarize(protoActivities[i]));
         }
         if (endIndex < protoActivities.Count)
         {
             builder.AddLayout(this.nextButtonLayout);
         }
         this.SubLayout = ScrollLayout.New(builder.BuildAnyLayout());
     }
 }
Пример #3
0
        private void initialize(bool showRatings)
        {
            List <Participation> participations = new List <Participation>(this.participations);

            if (!showRatings)
            {
                this.orderRandomly(participations);
            }

            Vertical_GridLayout_Builder gridBuilder = new Vertical_GridLayout_Builder();

            foreach (Participation participation in participations)
            {
                ParticipationView v = new ParticipationView(participation, this.scoreSummarizer, this.layoutStack, this.engine, showRatings);
                v.AddParticipationComment += Child_AddParticipationComment;
                gridBuilder.AddLayout(v);
            }
            if (!showRatings)
            {
                Button showRatings_button = new Button();
                showRatings_button.Clicked += ShowRatings_button_Clicked;
                showRatings_button.Text     = "Show Ratings";
                ButtonLayout showRatings_layout = new ButtonLayout(showRatings_button);
                gridBuilder.AddLayout(showRatings_layout);
            }
            this.SubLayout = ScrollLayout.New(gridBuilder.BuildAnyLayout());
        }
Пример #4
0
        public PostView(AnalyzedPost post)
        {
            this.post = post;
            Vertical_GridLayout_Builder mainBuilder  = new Vertical_GridLayout_Builder();
            Vertical_GridLayout_Builder titleBuilder = new Vertical_GridLayout_Builder();

            // post title
            foreach (AnalyzedString component in post.TitleComponents)
            {
                Label label = new Label();
                if (component.Score > 0)
                {
                    label.TextColor = Color.Green;
                }
                else
                {
                    if (component.Score < 0)
                    {
                        label.TextColor = Color.Red;
                    }
                    else
                    {
                        label.TextColor = Color.White;
                    }
                }
                label.BackgroundColor = Color.Black;
                TextblockLayout textBlockLayout = new TextblockLayout(label, 16, false, true);
                textBlockLayout.setText(component.Text);
                titleBuilder.AddLayout(textBlockLayout);
            }
            this.starButton = new Button();
            this.updateStarButton();
            this.starButton.Clicked += SaveButton_Clicked;

            GridLayout topGrid = GridLayout.New(new BoundProperty_List(1), BoundProperty_List.WithRatios(new List <double>()
            {
                5, 1
            }), LayoutScore.Zero);

            topGrid.AddLayout(titleBuilder.BuildAnyLayout());
            topGrid.AddLayout(new ButtonLayout(this.starButton));
            mainBuilder.AddLayout(topGrid);

            this.linkLayout = new TextblockLayout(post.Interaction.Post.Source, 16, false, true);
            this.linkLayout.setBackgroundColor(Color.Black);
            this.updateLinkColor();
            mainBuilder.AddLayout(this.linkLayout);

            Button openButton = new Button();

            mainBuilder.AddLayout(new ButtonLayout(openButton, "Open", 16));
            openButton.Clicked += OpenButton_Clicked;

            this.SubLayout = mainBuilder.BuildAnyLayout();
        }
Пример #5
0
        public LayoutChoice_Set Build()
        {
            Vertical_GridLayout_Builder gridBuilder = new Vertical_GridLayout_Builder().Uniform();

            foreach (string name in this.layoutNames)
            {
                LayoutChoice_Set subLayout = this.Get_ButtonLayout_By_Name(name);
                gridBuilder.AddLayout(subLayout);
            }
            return(gridBuilder.BuildAnyLayout());
        }
Пример #6
0
        public Confirm_BackupBeforeRecalculateEfficiency_Layout()
        {
            TextblockLayout warning = new TextblockLayout("You have requested to recalculate your efficiency using ActivityRecommender's latest algorithm. We will first " +
                                                          "make a backup of your data, and then reload from that backup and recalculate efficiency. Would you like to continue?");
            Button okButton = new Button();

            okButton.Clicked += Confirm;
            GridLayout_Builder builder = new Vertical_GridLayout_Builder()
                                         .AddLayout(warning)
                                         .AddLayout(new ButtonLayout(okButton, "Back up your data"));

            this.SubLayout = builder.BuildAnyLayout();
        }
Пример #7
0
        private void explainSuggestionQuality()
        {
            Vertical_GridLayout_Builder builder = new Vertical_GridLayout_Builder();

            builder.AddLayout(new TextblockLayout("Why:", this.maxFontSize));
            builder.AddLayout(new TextblockLayout("Suggestion quality: " + Math.Round(explanation.SuggestionValue, 3), this.maxFontSize));
            int childIndex = 0;

            foreach (Justification child in this.explanation.Reasons)
            {
                builder.AddLayouts(this.renderJustification(child, 0, this.explanation.Suggestion.ActivityDescriptor, childIndex));
                childIndex++;
            }
            this.suggestionQuality_container.SubLayout = builder.BuildAnyLayout();
        }
        private LayoutChoice_Set makeContent()
        {
            this.chooseFile_button          = new Button();
            this.chooseFile_button.Clicked += ChooseFile;
            ButtonLayout chooseLayout = new ButtonLayout(this.chooseFile_button, "Select file");

            GridLayout_Builder builder = new Vertical_GridLayout_Builder()
                                         .Uniform()
                                         .AddLayout(new TextblockLayout("The file to import should be of the form created by the Export feature " +
                                                                        "(and the name of file should start with \"ActivityData\")"));

            builder.AddLayout(chooseLayout);

            return(builder.BuildAnyLayout());
        }
        public New_ParticipationComment_Layout(Participation participation, LayoutStack layoutStack)
        {
            this.participation = participation;
            this.textBox       = new Editor();
            this.layoutStack   = layoutStack;
            Button saveButton = new Button();

            saveButton.Clicked += SaveButton_Clicked;
            GridLayout_Builder builder = new Vertical_GridLayout_Builder()
                                         .AddLayout(new TextblockLayout("Comment"))
                                         .AddLayout(new TextboxLayout(textBox))
                                         .AddLayout(new ButtonLayout(saveButton, "Save"));

            this.SubLayout = builder.BuildAnyLayout();
        }
Пример #10
0
        private LayoutChoice_Set makeSublayout()
        {
            TextblockLayout title    = new TextblockLayout("Export Protoactivities");
            TextblockLayout subtitle = new TextblockLayout("This feature exports a file containing all of your protoactivies. You can send them to your friends!");

            Button button = new Button();

            button.Clicked += Button_Clicked;
            ButtonLayout buttonLayout = new ButtonLayout(button, "Export");

            GridLayout_Builder builder = new Vertical_GridLayout_Builder()
                                         .AddLayout(title)
                                         .AddLayout(subtitle)
                                         .AddLayout(buttonLayout);

            return(builder.BuildAnyLayout());
        }
Пример #11
0
        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");
        }
Пример #12
0
        private void showPosts()
        {
            List <AnalyzedPost>         posts       = new List <AnalyzedPost>(this.analyzedPosts);
            Vertical_GridLayout_Builder gridBuilder = new Vertical_GridLayout_Builder();

            gridBuilder.AddLayout(this.downloadStatus_container);

            this.sortPosts(posts);
            posts = this.withoutDuplicateSources(posts);

            int maxCountToShow = 30;

            if (posts.Count > maxCountToShow)
            {
                posts = posts.GetRange(0, maxCountToShow);
            }
            double previousScore = double.NegativeInfinity;

            foreach (AnalyzedPost scoredPost in posts)
            {
                double thisScore = scoredPost.Score;
                if (thisScore != previousScore)
                {
                    string          text            = "Score: " + thisScore;
                    TextblockLayout textBlockLayout = new TextblockLayout(text, 30);
                    textBlockLayout.setBackgroundColor(Color.Black);
                    textBlockLayout.setTextColor(Color.White);
                    gridBuilder.AddLayout(textBlockLayout);

                    previousScore = thisScore;
                }
                PostView postView = new PostView(scoredPost);
                postView.PostClicked += PostView_PostClicked;
                postView.PostStarred += PostView_PostStarred;
                gridBuilder.AddLayout(postView);
            }

            LayoutChoice_Set scrollLayout = ScrollLayout.New(gridBuilder.BuildAnyLayout());

            this.downloadsStatus.NumUrlsDownloadedButNotShown = 0;
            this.downloadsStatus.NumFailed = 0;
            this.update_numCompletedDownloads_status();

            this.resultsLayout.SubLayout = scrollLayout;
        }
Пример #13
0
        private LayoutChoice_Set makeStarredScreen()
        {
            List <PostInteraction> starredPosts = this.getStarredPosts();

            if (starredPosts.Count < 1)
            {
                return(new TextblockLayout("No starred posts! First browse some new posts and star some of them"));
            }
            Vertical_GridLayout_Builder builder = new Vertical_GridLayout_Builder();

            foreach (PostInteraction interaction in starredPosts)
            {
                AnalyzedPost analyzed = this.analyzePost(interaction.Post);
                PostView     postView = new PostView(analyzed);
                postView.PostClicked += PostView_PostClicked;
                postView.PostStarred += PostView_PostStarred;
                builder.AddLayout(postView);
            }
            return(builder.BuildAnyLayout());
        }
        public Confirm_BackupBeforeDeleteActivity_Layout(Activity activity, LayoutStack layoutStack)
        {
            this.activity = activity;
            TextblockLayout warning = new TextblockLayout("You have requested to delete " + activity.Name + ". We will first make a backup of all of your data, and then reload from that backup and exclude " +
                                                          "this activity. Would you like to continue?");
            Button okButton = new Button();

            okButton.Clicked += ConfirmDeletion;

            LayoutChoice_Set credits = new CreditsButtonBuilder(layoutStack)
                                       .AddContribution(ActRecContributor.TOBY_HUANG, new DateTime(2021, 02, 23), "Suggested supporting deletion of created activities that have never been used, in case of mistakes in creating them")
                                       .Build();

            GridLayout_Builder builder = new Vertical_GridLayout_Builder()
                                         .AddLayout(warning)
                                         .AddLayout(new ButtonLayout(okButton, "Back up your data"))
                                         .AddLayout(credits);

            this.SubLayout = builder.BuildAnyLayout();
        }
        public ActivityListView(string name, List <Activity> activities, bool showSectionTitles)
        {
            this.showSectionTitles = showSectionTitles;

            List <Activity> openTodos      = new List <Activity>();
            List <Activity> completedTodos = new List <Activity>();
            List <Activity> nonTodos       = new List <Activity>();

            foreach (Activity activity in activities)
            {
                ToDo todo = activity as ToDo;
                if (todo != null)
                {
                    if (todo.IsCompleted())
                    {
                        completedTodos.Add(todo);
                    }
                    else
                    {
                        openTodos.Add(todo);
                    }
                }
                else
                {
                    nonTodos.Add(activity);
                }
            }


            GridLayout_Builder inlineBuilder = new Vertical_GridLayout_Builder().Uniform();

            this.addSection(inlineBuilder, nonTodos, "Non-ToDos:");
            this.addSection(inlineBuilder, openTodos, "Open ToDos:");
            this.addSection(inlineBuilder, completedTodos, "Completed ToDos:");

            this.SetContent(inlineBuilder.BuildAnyLayout());

            this.SetTitle(name);
        }
Пример #16
0
        private LayoutChoice_Set build()
        {
            GridLayout_Builder mainBuilder = new Vertical_GridLayout_Builder().Uniform();

            this.buttons            = new List <Button>();
            this.subtitles          = new List <TextblockLayout>();
            this.buttonDestinations = new Dictionary <Button, ValueProvider <StackEntry> >();
            for (int i = 0; i < this.buttonNameProviders.Count; i++)
            {
                Button button = new Button();
                button.Clicked += Button_Clicked;
                this.buttons.Add(button);

                ButtonLayout    buttonLayout   = new ButtonLayout(button);
                TextblockLayout subtitleLayout = new TextblockLayout();
                subtitleLayout.AlignHorizontally(TextAlignment.Center);
                subtitleLayout.AlignVertically(TextAlignment.Center);
                subtitleLayout.ScoreIfEmpty = false;
                this.subtitles.Add(subtitleLayout);

                BoundProperty_List columnWidths = new BoundProperty_List(2);
                columnWidths.SetPropertyScale(0, 2);
                columnWidths.SetPropertyScale(1, 1);
                columnWidths.BindIndices(0, 1);
                GridLayout entryGrid = GridLayout.New(new BoundProperty_List(1), columnWidths, LayoutScore.Get_UnCentered_LayoutScore(1));
                entryGrid.AddLayout(buttonLayout);
                entryGrid.AddLayout(subtitleLayout);

                LayoutUnion content = new LayoutUnion(entryGrid, buttonLayout);
                mainBuilder.AddLayout(content);

                ValueProvider <StackEntry> destinationProvider = destinationProviders[i];
                this.buttonDestinations[button] = destinationProvider;
            }
            return(LayoutCache.For(mainBuilder.BuildAnyLayout()));
        }
        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();
        }
        public LayoutChoice_Set MakeLayout(LayoutStack layoutStack)
        {
            Vertical_GridLayout_Builder builder = new Vertical_GridLayout_Builder();

            if (this.SuggestedBadIdea)
            {
                string apology;
                if (this.PredictedValue > 1)
                {
                    // A fun idea that we didn't expect the user to do
                    // We expected the user to get distracted while considering the fun idea
                    apology = "Sorry, I didn't expect you to actually do this! I thought that this suggestion would help you think of a better idea!";
                }
                else
                {
                    // An annoying idea that we didn't expect the user to do
                    // We expected the user to remember how annoying this was and to avoid it and things like it
                    apology = "Sorry, I didn't expect you to actually do this! I thought that this suggestion would remind you to look harder for something better!";
                }
                builder.AddLayout(new TextblockLayout(apology));
            }
            builder.AddLayout(new TextblockLayout(ChosenActivity.Name));
            builder.AddLayout(new TextblockLayout("From " + this.StartDate + " to " + this.EndDate + ", " + ParticipationDurationDividedByAverage + " as long as average. I predict:"));

            GridLayout_Builder nowBuilder = new Horizontal_GridLayout_Builder().Uniform();

            nowBuilder.AddLayout(
                new Vertical_GridLayout_Builder().Uniform()
                .AddLayout(
                    new HelpButtonLayout("Fun (vs average):",
                                         new TextblockLayout("This column shows the amount of happiness you are expected to have while doing this activity at this time, divided by the average amount of happiness you usually have doing other things"),
                                         layoutStack)
                    )
                .AddLayout(coloredRatio(PredictedValue, ComparisonPredictedValue, PredictedCurrentValueStddev))
                .Build()
                );
            nowBuilder.AddLayout(
                new Vertical_GridLayout_Builder().Uniform()
                .AddLayout(
                    new HelpButtonLayout("Future Fun (days):",
                                         new TextblockLayout("This column shows an estimate of the net present value of your happiness at this time after doing this activity, compared to what it usually is. " +
                                                             "This is very similar to computing how many days of happiness you will gain or lose over the next " + Math.Round(UserPreferences.DefaultPreferences.HalfLife.TotalDays / Math.Log(2), 0) +
                                                             " days after doing this."),
                                         layoutStack)
                    )
                .AddLayout(signedColoredValue(ExpectedFutureFun, ComparisonExpectedFutureFun, ExpectedFutureFunStddev))
                .Build()
                );
            nowBuilder.AddLayout(
                new Vertical_GridLayout_Builder().Uniform()
                .AddLayout(
                    new HelpButtonLayout("Future Efficiency (hours):",
                                         new TextblockLayout("This column shows an estimate of the net present value of your efficiency at this time after doing this activity, compared to what it usually is. " +
                                                             "This is very similar to computing how many hours of efficiency you will gain or lose over the next " + Math.Round(UserPreferences.DefaultPreferences.EfficiencyHalflife.TotalDays / Math.Log(2), 0) +
                                                             " days after doing this."),
                                         layoutStack)
                    )
                .AddLayout(signedColoredValue(ExpectedEfficiency, ComparisonExpectedEfficiency, ExpectedEfficiencyStddev))
                .Build()
                );
            builder.AddLayout(nowBuilder.Build());

            builder.AddLayout(new TextblockLayout("If you had done this at " + this.ComparisonDate + ":"));

            GridLayout_Builder laterBuilder = new Horizontal_GridLayout_Builder().Uniform();

            laterBuilder.AddLayout(coloredRatio(ComparisonPredictedValue, 1, 0));
            laterBuilder.AddLayout(signedColoredValue(ComparisonExpectedFutureFun, 1, 0));
            laterBuilder.AddLayout(signedColoredValue(ComparisonExpectedEfficiency, 0, 0));
            builder.AddLayout(laterBuilder.Build());

            ActivityRequest request = new ActivityRequest();

            request.ActivityToBeat = this.ChosenActivity.MakeDescriptor();
            request.Date           = this.StartDate;
            //request.RequestedProcessingTime = TimeSpan.FromSeconds(0.5);
            ActivitiesSuggestion suggestion       = this.engine.MakeRecommendation(request);
            Activity             betterActivity   = this.ActivityDatabase.ResolveDescriptor(suggestion.ActivityDescriptors[0]);
            Prediction           betterPrediction = this.engine.Get_OverallHappiness_ParticipationEstimate(betterActivity, request);
            string       redirectionText;
            Color        redirectionColor;
            Distribution betterFutureHappinessImprovementInDays = this.engine.compute_longtermValue_increase_in_days(betterPrediction.Distribution, this.StartDate, this.StartDate);
            double       improvementInDays = Math.Round(betterFutureHappinessImprovementInDays.Mean - this.ExpectedFutureFun, 1);

            if (improvementInDays <= 0)
            {
                string noIdeasText = "I don't have any better suggestions for things to do at this time.";
                if (ExpectedFutureFun >= 0)
                {
                    if (PredictedValue >= 1)
                    {
                        redirectionText = "Nice! " + noIdeasText; // Happy now, happy later
                    }
                    else
                    {
                        redirectionText = noIdeasText + " Sorry!"; // Happy later, not happy now
                    }
                }
                else
                {
                    if (PredictedValue >= 1)
                    {
                        redirectionText = noIdeasText; // Happy now, not happy later
                    }
                    else
                    {
                        redirectionText = "How about adding a new activity? " + noIdeasText; // Not happy now or later
                    }
                }
                redirectionColor = Color.Green;
            }
            else
            {
                string improvementText = improvementInDays.ToString();
                if (this.Suggested)
                {
                    redirectionText  = "I thought of a better idea: " + betterActivity.Name + ", better by +" + improvementText + " days fun. Sorry for not mentioning this earlier!";
                    redirectionColor = Color.Yellow;
                }
                else
                {
                    if (ExpectedFutureFun >= 0)
                    {
                        redirectionText  = "I suggest that " + betterActivity.Name + " would be even better: +" + improvementText + " days fun.";
                        redirectionColor = Color.Yellow;
                    }
                    else
                    {
                        redirectionText  = "I suggest that " + betterActivity.Name + " would improve your future happiness by " + improvementText + " days.";
                        redirectionColor = Color.Red;
                    }
                }
            }
            builder.AddLayout(new TextblockLayout(redirectionText, redirectionColor));


            return(builder.BuildAnyLayout());
        }
Пример #19
0
        public Full_RequestSuggestion_Layout(ActivityDatabase activityDatabase, bool allowRequestingActivitiesDirectly, bool allowMultipleSuggestionTypes, bool vertical,
                                             int numChoicesPerSuggestion, Engine engine, LayoutStack layoutStack)
        {
            this.numChoicesPerSuggestion = numChoicesPerSuggestion;
            Button suggestionButton = new Button();

            suggestionButton.Clicked += SuggestBestActivity_Clicked;
            ButtonLayout suggest_maxLongtermHappiness_button;

            if (allowMultipleSuggestionTypes)
            {
                suggest_maxLongtermHappiness_button = new ButtonLayout(suggestionButton, "Best");
            }
            else
            {
                suggest_maxLongtermHappiness_button = new ButtonLayout(suggestionButton, "Suggest");
            }
            LayoutChoice_Set suggestButton_layout;

            if (allowRequestingActivitiesDirectly)
            {
                GridLayout_Builder builder = new Vertical_GridLayout_Builder().Uniform().AddLayout(suggest_maxLongtermHappiness_button);
                if (allowMultipleSuggestionTypes)
                {
                    Button suggestionButton2 = new Button();
                    suggestionButton2.Clicked += SuggestMostLikelyActivity_Clicked;
                    ButtonLayout suggest_mostLikely_button = new ButtonLayout(suggestionButton2, "Most Likely");
                    builder.AddLayout(suggest_mostLikely_button);

                    Button suggestionButton3 = new Button();
                    suggestionButton3.Clicked += SuggestMostEfficientActivity_Clicked;
                    ButtonLayout suggest_mostEfficient_button = new ButtonLayout(suggestionButton3, "Most Future Efficiency");
                    builder.AddLayout(suggest_mostEfficient_button);
                }
                suggestButton_layout = builder.BuildAnyLayout();
            }
            else
            {
                suggestButton_layout = suggest_maxLongtermHappiness_button;
            }

            this.categoryBox = new ActivityNameEntryBox("Category:", activityDatabase, layoutStack);
            this.categoryBox.Placeholder("(Optional)");
            this.activityDatabase = activityDatabase;
            this.engine           = engine;
            this.layoutStack      = layoutStack;


            if (!allowRequestingActivitiesDirectly)
            {
                this.SubLayout = suggest_maxLongtermHappiness_button;
            }
            else
            {
                GridLayout configurationLayout = GridLayout.New(BoundProperty_List.Uniform(2), new BoundProperty_List(1), LayoutScore.Zero);
                configurationLayout.AddLayout(this.categoryBox);


                this.atLeastAsFunAs_button     = new Button();
                atLeastAsFunAs_button.Clicked += RequestAsFunAs_Button_Clicked;
                configurationLayout.AddLayout(new TitledControl("As fun as:", new ButtonLayout(atLeastAsFunAs_button)));

                if (vertical)
                {
                    GridLayout verticalContentLayout = GridLayout.New(new BoundProperty_List(2), new BoundProperty_List(1), LayoutScore.Get_UnCentered_LayoutScore(2));
                    verticalContentLayout.AddLayout(configurationLayout);
                    verticalContentLayout.AddLayout(suggestButton_layout);
                    this.SubLayout = verticalContentLayout;
                }
                else
                {
                    GridLayout horizontalContentLayout = GridLayout.New(new BoundProperty_List(1), new BoundProperty_List(2), LayoutScore.Get_UnCentered_LayoutScore(1));
                    horizontalContentLayout.AddLayout(suggestButton_layout);
                    horizontalContentLayout.AddLayout(configurationLayout);

                    this.SubLayout = horizontalContentLayout;
                }
                this.specify_AtLeastAsFunAs_Layout = new Specify_AtLeastAsFunAs_Layout(this.activityDatabase, this.layoutStack);
                this.update_atLeastAsFunAs_activity();
            }
        }
Пример #20
0
        public SuggestionsView(ActivityRecommender recommenderToInform, LayoutStack layoutStack, ActivityDatabase activityDatabase, Engine engine) : base("Get Suggestions")
        {
            this.TitleLayout.AlignVertically(TextAlignment.Center);
            this.activityDatabase = activityDatabase;
            this.engine           = engine;
            this.recommender      = recommenderToInform;

            this.layoutStack = layoutStack;

            this.messageLayout = new TextblockLayout("").AlignHorizontally(TextAlignment.Center).AlignVertically(TextAlignment.Center);

            this.requestSuggestion_layout = new RequestSuggestion_Layout(activityDatabase, true, true, false, 3, engine, layoutStack);
            this.requestSuggestion_layout.RequestSuggestion += RequestSuggestion_layout_RequestSuggestion;
            this.askWhatIsNext_layout = new TextblockLayout().AlignHorizontally(TextAlignment.Center).AlignVertically(TextAlignment.Center);

            LayoutChoice_Set helpWindow = (new HelpWindowBuilder()).AddMessage("Use this screen to ask for activity recommendations from among activities you have said that you like.")
                                          .AddMessage("By default, the recommendation will attempt to maximize your long-term happiness.")
                                          .AddMessage("The recommendation may be slightly randomized if ActivityRecommender doesn't have enough time to consider every activity within a couple seconds.")
                                          .AddMessage("If you are sure that you want an activity from a certain category, then you can enter its name into the category box, and ActivityRecommender will make sure " +
                                                      "that your suggestion will be an instance of that activity. For example, if you have previously entered an activity named Checkers and listed it as a child activity " +
                                                      "of Game, then when you ask for a Game, one possible suggestion will be Checkers.")
                                          .AddMessage("If you're looking for an activity to have a high amount of enjoyability right now, then think of what you would do if you couldn't ask ActivityRecommender for " +
                                                      "help, and type the name of that activity into the second box. If you do, then ActivityRecommender will make sure to provide a suggestion that it thinks you will like as " +
                                                      "much as it thinks you will like as much as the one you entered.")
                                          .AddMessage("Then, push one of the Suggest buttons")
                                          .AddMessage("You can request either the activity that is expected to maximize the long-term value to you, or " +
                                                      "the activity that ActivityRecommender thinks that you're most likely to do, among the activities satisfying your other criteria.")
                                          .AddMessage("Each suggestion will list an activity name, followed by the time to start the activity, an estimate of the probability that you will actually do that activity, " +
                                                      "and an estimate of the rating that you'd be expected to give to that activity.")
                                          .AddMessage("If you don't like a suggestion, press the X button next to it. The duration between when you ask for a suggestion and when you press the X is considered to " +
                                                      "be worth 0 happiness to you (unless you already recorded having done a participation during that time, in which case the duration between your latest completed participation and " +
                                                      "when you press the X is considered to be worth 0 happiness to you), so ActivityRecommender tries to avoid giving you too many suggestions that you don't take. However, if there's " +
                                                      "a certain activity that ActivityRecommender thinks would be awesome for you despite your current disinterest, then ActivityRecommender may repeat its suggestions a few times, in " +
                                                      "an effort to get you to reconsider.")
                                          .AddMessage("You can also plan out several participations in a row by pressing the Suggest button multiple times in a row.")
                                          .AddMessage("Whenever you record a participation matching the first suggestion in the list, then that suggestion will be removed.")
                                          .AddMessage("Enjoy!")
                                          .AddLayout(new CreditsButtonBuilder(layoutStack)
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2019, 8, 1), "Suggested that when the user asks for a suggestion at least as fun as a certain activity, ActivityRecommender should always suggest a different activity than the one they mentioned")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 7, 18), "Pointed out that buttons on iOS weren't visually responding to touch")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2020, 12, 17), "Mentioned that the suggestions were sometimes more repetitive than desired")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 3, 26), "Suggested that the suggestions view could use the same descriptions as the participation entry view")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 4, 24), "Asked for the suggestions to be less repetitive again")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 4, 30), "Asked for the suggestions view to display multiple suggestions at once")
                                                     .AddContribution(ActRecContributor.ANNI_ZHANG, new DateTime(2021, 7, 2), "Suggested shorter suggestion button text")
                                                     .Build()
                                                     )
                                          .Build();

            this.helpButton_layout         = new HelpButtonLayout(helpWindow, this.layoutStack);
            this.experimentButton          = new Button();
            this.startExperiment_layout    = new ButtonLayout(this.experimentButton, "Efficiency Experiment");
            this.experimentButton.Clicked += ExperimentButton_Clicked;
            this.bottomLayout              = new Horizontal_GridLayout_Builder().Uniform().AddLayout(this.startExperiment_layout).AddLayout(this.helpButton_layout).Build();

            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 ask for suggestions of what to do."));
            noActivities_help_builder.AddLayout(
                new HelpButtonLayout("Not a calendar: no interruptions, no reminders. Exciting!",
                                     new HelpWindowBuilder()
                                     .AddMessage("ActivityRecommender doesn't offer reminders at prescheduled times because that's boring. " +
                                                 "If you're looking for a simple scheduling algorithm, you may be interested in a calendar. ActivityRecommender is more like " +
                                                 "a friend, full of cool, interesting, somewhat randomized ideas based on information about you.")
                                     .Build()
                                     ,
                                     layoutStack
                                     )
                );
            noActivities_help_builder.AddLayout(
                new HelpButtonLayout("Suggestions get better over time",
                                     new HelpWindowBuilder()
                                     .AddMessage("The more data you enter into ActivityRecommender, the better its suggestions will be.")
                                     .AddMessage("When you haven't entered much data, ActivityRecommender primarily suggests activities that resemble other " +
                                                 "activities that you like. For example, if you've mentioned that Soccer and Jogging are relevant to you and that " +
                                                 "both of them are exercise, and if you tend to like Soccer, then ActivityRecommender may also recomend Jogging to you.")
                                     .AddMessage("After you've entered a little bit of data, ActivityRecommender tends to suggest activities that you like. " +
                                                 "If you tend to enjoy playing Soccer, then ActivityRecommender will recommend that!")
                                     .AddMessage("After you've entered more data, ActivityRecommender will be able to look for trends in how happy you are after " +
                                                 "having done various activities, and incorporate that too. For example, if you like to Eat Ice Cream, but eating ice cream" +
                                                 "late at night tends to make it hard to sleep, then maybe ActivityRecommender won't suggest it late at night even if you " +
                                                 "like it.")
                                     .AddMessage("After you've entered a lot of data, ActivityRecommender will even be able to look for trends in how happy you are " +
                                                 "after suggesting various activities. If you like walking but you don't have any ideas of where to walk, then maybe " +
                                                 "ActivityRecommender will notice that suggesting Walking never works. However, if you like buying things at the store, then " +
                                                 "perhaps ActivityRecommender will suggest that instead, if that causes you to walk to the store to buy something.")
                                     .Build()
                                     ,
                                     layoutStack
                                     )
                );
            noActivities_help_builder.AddLayout(new TextblockLayout("Before you can ask for a suggestion, ActivityRecommender needs you to go back " +
                                                                    "and add some activities first. Here is a convenient button for jumping directly to the Activities screen:"));
            Button visitActivities_button = new Button();

            visitActivities_button.Text     = "Activities";
            visitActivities_button.Clicked += VisitActivities_button_Clicked;
            noActivities_help_builder.AddLayout(new ButtonLayout(visitActivities_button));

            this.noActivities_explanationLayout = noActivities_help_builder.BuildAnyLayout();

            this.UpdateSuggestions();
        }
Пример #21
0
        public SuggestionView(ActivitiesSuggestion suggestion, bool isFirstSuggestion, Dictionary <ActivitySuggestion, bool> repeatingDeclinedSuggestion, LayoutStack layoutStack)
        {
            this.suggestion  = suggestion;
            this.layoutStack = layoutStack;

            bool allWorseThanAverage = true;

            foreach (ActivitySuggestion child in suggestion.Children)
            {
                if (!child.WorseThanRootActivity)
                {
                    allWorseThanAverage = false;
                }
            }

            GridLayout_Builder fullBuilder   = new Vertical_GridLayout_Builder();
            string             startTimeText = suggestion.Children[0].StartDate.ToString("HH:mm");

            bool badSuggestion = (allWorseThanAverage && suggestion.Skippable);

            if (badSuggestion)
            {
                fullBuilder.AddLayout(new TextblockLayout("Best ideas at " + startTimeText + ":", 24).AlignHorizontally(TextAlignment.Center));
            }
            else
            {
                fullBuilder.AddLayout(new TextblockLayout("At " + startTimeText + ":", 24).AlignHorizontally(TextAlignment.Center));
            }

            List <LayoutChoice_Set> specificFont_contentChoices = new List <LayoutChoice_Set>(); // list of layouts we might use, each with a different font size

            this.explainButtons = new Dictionary <Button, ActivitySuggestion>();
            this.doButtons      = new Dictionary <Button, ActivitySuggestion>();
            for (int mainFontSize = 20; mainFontSize >= 12; mainFontSize -= 8)
            {
                // grid containing the specific activities the user could do
                GridLayout activityOptionsGrid = GridLayout.New(new BoundProperty_List(3), BoundProperty_List.Uniform(suggestion.Children.Count), LayoutScore.Zero);

                for (int i = 0; i < suggestion.Children.Count; i++)
                {
                    ActivitySuggestion child = suggestion.Children[i];

                    // set up the options for the text
                    string          mainText  = this.summarize(child, repeatingDeclinedSuggestion[child]);
                    TextblockLayout mainBlock = new TextblockLayout(mainText, mainFontSize);
                    TextAlignment   horizontalAlignment;
                    TextAlignment   verticalAlignment;
                    if (i == 0)
                    {
                        horizontalAlignment = TextAlignment.Start;
                        if (suggestion.Children.Count == 1)
                        {
                            verticalAlignment = TextAlignment.Start;
                        }
                        else
                        {
                            verticalAlignment = TextAlignment.End;
                        }
                    }
                    else
                    {
                        if (i == suggestion.Children.Count - 1)
                        {
                            horizontalAlignment = TextAlignment.End;
                            verticalAlignment   = TextAlignment.Start;
                        }
                        else
                        {
                            horizontalAlignment = TextAlignment.Center;
                            verticalAlignment   = TextAlignment.Center;
                        }
                    }
                    mainBlock.AlignHorizontally(horizontalAlignment);
                    mainBlock.AlignVertically(verticalAlignment);
                    activityOptionsGrid.PutLayout(mainBlock, i, 0);

                    // set up the buttons
                    GridLayout_Builder buttonsBuilder = new Horizontal_GridLayout_Builder().Uniform();
                    double             buttonFontSize = mainFontSize * 0.9;
                    // make a doNow button if needed
                    if (isFirstSuggestion)
                    {
                        Button doNowButton = new Button();
                        doNowButton.Clicked        += DoNowButton_Clicked;
                        this.doButtons[doNowButton] = child;
                        ButtonLayout doButtonLayout = new ButtonLayout(doNowButton, "OK", buttonFontSize);
                        buttonsBuilder.AddLayout(doButtonLayout);
                    }
                    if (child.PredictedScoreDividedByAverage != null)
                    {
                        Button explainButton = new Button();
                        explainButton.Clicked += explainButton_Clicked;
                        this.explainButtons[explainButton] = child;
                        ButtonLayout explainLayout = new ButtonLayout(explainButton, "?", buttonFontSize);
                        buttonsBuilder.AddLayout(explainLayout);
                    }
                    activityOptionsGrid.PutLayout(buttonsBuilder.BuildAnyLayout(), i, 1);
                    if (child.ExpectedReaction != null)
                    {
                        TextblockLayout reactionLayout = new TextblockLayout(child.ExpectedReaction, buttonFontSize * 0.9);
                        reactionLayout.AlignHorizontally(horizontalAlignment);
                        reactionLayout.AlignVertically(verticalAlignment);
                        activityOptionsGrid.PutLayout(reactionLayout, i, 2);
                    }
                }

                LayoutChoice_Set optionsAtThisFontSize;
                if (badSuggestion)
                {
                    // If the suggestion is bad and we don't really want the user to do it, then we also show the user some convenient buttons for making more activities
                    GridLayout wrapper = GridLayout.New(new BoundProperty_List(1), BoundProperty_List.WithRatios(new List <double>()
                    {
                        suggestion.Children.Count, 1
                    }), LayoutScore.Zero);
                    wrapper.AddLayout(activityOptionsGrid);                            // activity suggestions
                    wrapper.AddLayout(this.make_otherActivities_layout(mainFontSize)); // layout for making new activities
                    optionsAtThisFontSize = wrapper;
                }
                else
                {
                    // If the suggestion isn't bad, then the options we give are just the activities being suggested
                    optionsAtThisFontSize = activityOptionsGrid;
                }
                specificFont_contentChoices.Add(optionsAtThisFontSize);
            }

            LayoutChoice_Set contentGrid = LayoutUnion.New(specificFont_contentChoices);

            // Add cancel buttons to the bottom
            this.cancelButton                         = new Button();
            this.cancelButton.Clicked                += cancelButton_Click;
            this.explainWhyYouCantSkipButton          = new Button();
            this.explainWhyYouCantSkipButton.Clicked += ExplainWhyYouCantSkipButton_Clicked;
            ButtonLayout cancelLayout;

            if (suggestion.Skippable)
            {
                cancelLayout = new ButtonLayout(this.cancelButton, "X");
            }
            else
            {
                cancelLayout = new ButtonLayout(this.explainWhyYouCantSkipButton, "!");
            }



            fullBuilder.AddLayout(contentGrid)
            .AddLayout(cancelLayout);

            this.SubLayout = fullBuilder.BuildAnyLayout();
        }