package gui // viewGeneticReferencesGui.go implements pages to display information about genetic diseases and traits import "fyne.io/fyne/v2" import "fyne.io/fyne/v2/widget" import "fyne.io/fyne/v2/theme" import "fyne.io/fyne/v2/container" import "fyne.io/fyne/v2/layout" import "seekia/resources/geneticReferences/locusMetadata" import "seekia/resources/geneticReferences/traits" import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/internal/helpers" import "strings" import "slices" import "errors" func setViewMonogenicDiseaseDetailsPage(window fyne.Window, diseaseName string, previousPage func()){ currentPage := func(){setViewMonogenicDiseaseDetailsPage(window, diseaseName, previousPage)} title := getPageTitleCentered("Monogenic Disease Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) diseaseObject, err := monogenicDiseases.GetMonogenicDiseaseObject(diseaseName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } diseaseGeneName := diseaseObject.GeneName diseaseIsDominantOrRecessive := diseaseObject.DominantOrRecessive diseaseDescription := diseaseObject.DiseaseDescription diseaseReferencesMap := diseaseObject.References diseaseNameLabel := widget.NewLabel("Disease Name:") diseaseNameText := getBoldLabel(diseaseName) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, layout.NewSpacer()) geneNameLabel := widget.NewLabel("Gene Name:") geneNameText := getBoldLabel(diseaseGeneName) geneNameRow := container.NewHBox(layout.NewSpacer(), geneNameLabel, geneNameText, layout.NewSpacer()) dominantOrRecessiveLabel := widget.NewLabel("Dominant or Recessive?:") dominantOrRecessiveText := getBoldLabel(diseaseIsDominantOrRecessive) dominantOrRecessiveRow := container.NewHBox(layout.NewSpacer(), dominantOrRecessiveLabel, dominantOrRecessiveText, layout.NewSpacer()) diseaseDescriptionTrimmed, _, err := helpers.TrimAndFlattenString(diseaseDescription, 10) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } diseaseDescriptionLabel := widget.NewLabel("Description:") diseaseDescriptionText := getBoldLabel(diseaseDescriptionTrimmed) viewDiseaseDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewTextPage(window, "Disease Description", diseaseDescription, false, currentPage) }) diseaseDescriptionRow := container.NewHBox(layout.NewSpacer(), diseaseDescriptionLabel, diseaseDescriptionText, viewDiseaseDescriptionButton, layout.NewSpacer()) viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Monogenic Disease", diseaseReferencesMap, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), geneNameRow, widget.NewSeparator(), dominantOrRecessiveRow, widget.NewSeparator(), diseaseDescriptionRow, widget.NewSeparator(), viewReferencesButton) setPageContent(page, window) } func setViewMonogenicDiseaseVariantDetailsPage(window fyne.Window, diseaseName string, variantIdentifier string, previousPage func()){ currentPage := func(){setViewMonogenicDiseaseVariantDetailsPage(window, diseaseName, variantIdentifier, previousPage)} title := getPageTitleCentered("Viewing Monogenic Disease Variant Details") backButton := getBackButtonCentered(previousPage) diseaseNameLabel := widget.NewLabel("Disease Name:") diseaseNameText := getBoldLabel(diseaseName) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, layout.NewSpacer()) variantObject, err := monogenicDiseases.GetMonogenicDiseaseVariantObject(diseaseName, variantIdentifier) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } variantNamesList := variantObject.VariantNames nucleotideChange := variantObject.NucleotideChange aminoAcidChange := variantObject.AminoAcidChange variantRSID := variantObject.VariantRSID variantEffectIsMild := variantObject.EffectIsMild referencesMap := variantObject.References variantRSIDsList := []int64{variantRSID} // We add aliases to variantRSIDsList anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(variantRSID) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (anyAliasesExist == true){ variantRSIDsList = append(variantRSIDsList, rsidAliasesList...) } getVariantNamesLabelText := func()string{ if(len(variantNamesList) == 1){ return "Variant Name:" } return "Variant Names:" } variantNamesLabelText := getVariantNamesLabelText() variantNamesListString := strings.Join(variantNamesList, ", ") variantNamesLabel := widget.NewLabel(variantNamesLabelText) variantNamesText := getBoldLabel(variantNamesListString) variantNamesRow := container.NewHBox(layout.NewSpacer(), variantNamesLabel, variantNamesText, layout.NewSpacer()) nucleotideChangeLabel := widget.NewLabel("Nucleotide Change:") nucleotideChangeText := getBoldLabel(nucleotideChange) nucleotideChangeRow := container.NewHBox(layout.NewSpacer(), nucleotideChangeLabel, nucleotideChangeText, layout.NewSpacer()) page := container.NewVBox(title, backButton, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), variantNamesRow, widget.NewSeparator(), nucleotideChangeRow, widget.NewSeparator()) if (aminoAcidChange != ""){ aminoAcidChangeLabel := widget.NewLabel("Amino Acid Change:") aminoAcidChangeText := getBoldLabel(aminoAcidChange) aminoAcidChangeRow := container.NewHBox(layout.NewSpacer(), aminoAcidChangeLabel, aminoAcidChangeText, layout.NewSpacer()) page.Add(aminoAcidChangeRow) page.Add(widget.NewSeparator()) } getVariantRSIDsLabelText := func()string{ if (len(variantRSIDsList) == 1){ return "Variant rsID:" } return "Variant rsIDs:" } variantRSIDsLabelText := getVariantRSIDsLabelText() variantRSIDStringsList := make([]string, 0, len(variantRSIDsList)) for _, variantRSID := range variantRSIDsList{ variantRSIDString := helpers.ConvertInt64ToString(variantRSID) variantRSIDName := "rs" + variantRSIDString variantRSIDStringsList = append(variantRSIDStringsList, variantRSIDName) } variantRSIDsListString := strings.Join(variantRSIDStringsList, ", ") variantRSIDsLabel := widget.NewLabel(variantRSIDsLabelText) variantRSIDsText := getBoldLabel(variantRSIDsListString) variantRSIDsRow := container.NewHBox(layout.NewSpacer(), variantRSIDsLabel, variantRSIDsText, layout.NewSpacer()) page.Add(variantRSIDsRow) page.Add(widget.NewSeparator()) effectIsMildString := helpers.ConvertBoolToYesOrNoString(variantEffectIsMild) effectIsMildLabel := widget.NewLabel("Effect is Mild:") effectIsMildText := getBoldLabel(effectIsMildString) effectIsMildHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setVariantEffectIsMildExplainerPage(window, currentPage) }) effectIsMildRow := container.NewHBox(layout.NewSpacer(), effectIsMildLabel, effectIsMildText, effectIsMildHelpButton, layout.NewSpacer()) page.Add(effectIsMildRow) page.Add(widget.NewSeparator()) viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Variant", referencesMap, currentPage) })) page.Add(viewReferencesButton) setPageContent(page, window) } func setViewPolygenicDiseaseDetailsPage(window fyne.Window, diseaseName string, previousPage func()){ currentPage := func(){setViewPolygenicDiseaseDetailsPage(window, diseaseName, previousPage)} title := getPageTitleCentered("Disease Details - " + diseaseName) backButton := getBackButtonCentered(previousPage) diseaseObject, err := polygenicDiseases.GetPolygenicDiseaseObject(diseaseName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } diseaseDescription := diseaseObject.DiseaseDescription effectedSex := diseaseObject.EffectedSex diseaseReferencesMap := diseaseObject.References diseaseNameLabel := widget.NewLabel("Disease Name:") diseaseNameText := getBoldLabel(diseaseName) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, layout.NewSpacer()) getEffectedSexTextLabelText := func()string{ if (effectedSex == "Both"){ return "Male and Female" } return effectedSex } effectedSexTextLabelText := getEffectedSexTextLabelText() effectedSexLabel := widget.NewLabel("Effected Sex:") effectedSexText := getBoldLabel(effectedSexTextLabelText) effectedSexRow := container.NewHBox(layout.NewSpacer(), effectedSexLabel, effectedSexText, layout.NewSpacer()) diseaseDescriptionTrimmed, _, err := helpers.TrimAndFlattenString(diseaseDescription, 10) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } diseaseDescriptionLabel := widget.NewLabel("Description:") diseaseDescriptionText := getBoldLabel(diseaseDescriptionTrimmed) viewDiseaseDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewTextPage(window, "Disease Description", diseaseDescription, false, currentPage) }) diseaseDescriptionRow := container.NewHBox(layout.NewSpacer(), diseaseDescriptionLabel, diseaseDescriptionText, viewDiseaseDescriptionButton, layout.NewSpacer()) viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Polygenic Disease", diseaseReferencesMap, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), effectedSexRow, widget.NewSeparator(), diseaseDescriptionRow, widget.NewSeparator(), viewReferencesButton) setPageContent(page, window) } func setViewPolygenicDiseaseLocusDetailsPage(window fyne.Window, diseaseName string, locusIdentifier string, previousPage func()){ currentPage := func(){setViewPolygenicDiseaseLocusDetailsPage(window, diseaseName, locusIdentifier, previousPage)} title := getPageTitleCentered("Viewing Locus Details") backButton := getBackButtonCentered(previousPage) locusObject, err := polygenicDiseases.GetPolygenicDiseaseLocusObject(diseaseName, locusIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } locusRSID := locusObject.LocusRSID locusReferencesMap := locusObject.References locusRSIDsList := []int64{locusRSID} // We add aliases to locusRSIDsList anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(locusRSID) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (anyAliasesExist == true){ locusRSIDsList = append(locusRSIDsList, rsidAliasesList...) } metadataExists, locusMetadataObject, err := locusMetadata.GetLocusMetadata(locusRSID) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (metadataExists == false){ setErrorEncounteredPage(window, errors.New("setViewPolygenicDiseaseLocusDetailsPage called with locusRSID missing from locusMetadata."), previousPage) return } diseaseNameLabel := widget.NewLabel("Disease Name:") diseaseNameText := getBoldLabel(diseaseName) diseaseNameRow := container.NewHBox(layout.NewSpacer(), diseaseNameLabel, diseaseNameText, layout.NewSpacer()) getLocusNamesLabelText := func()string{ if(len(locusRSIDsList) == 1){ return "Locus Name:" } return "Locus Names:" } locusNamesLabelText := getLocusNamesLabelText() locusRSIDStringsList := make([]string, 0, len(locusRSIDsList)) for _, locusRSID := range locusRSIDsList{ locusRSIDString := helpers.ConvertInt64ToString(locusRSID) locusRSIDName := "rs" + locusRSIDString locusRSIDStringsList = append(locusRSIDStringsList, locusRSIDName) } locusNamesListString := strings.Join(locusRSIDStringsList, ", ") locusNamesLabel := widget.NewLabel(locusNamesLabelText) locusNamesText := getBoldLabel(locusNamesListString) locusNamesRow := container.NewHBox(layout.NewSpacer(), locusNamesLabel, locusNamesText, layout.NewSpacer()) getLocusGeneNameLabelValue := func()string{ locusGeneInfoIsKnown := locusMetadataObject.GeneInfoIsKnown if (locusGeneInfoIsKnown == false){ return "Unknown" } locusGeneExists := locusMetadataObject.GeneExists if (locusGeneExists == false){ return "None" } locusGeneName := locusMetadataObject.GeneNamesList[0] return locusGeneName } locusGeneNameLabelValue := getLocusGeneNameLabelValue() geneNameLabel := widget.NewLabel("Gene Name:") geneNameText := getBoldLabel(locusGeneNameLabelValue) geneNameRow := container.NewHBox(layout.NewSpacer(), geneNameLabel, geneNameText, layout.NewSpacer()) viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Locus", locusReferencesMap, currentPage) })) getBasePairsGrid := func()(*fyne.Container, error){ locusRiskWeightsMap := locusObject.RiskWeightsMap locusBasePairProbabilitiesMap := locusObject.BasePairProbabilitiesMap riskWeightLabel := getItalicLabelCentered("Risk Weight") probabilityLabel := getItalicLabelCentered("Probability Of Weight") riskWeightColumn := container.NewVBox(riskWeightLabel, widget.NewSeparator()) riskWeightProbabilityColumn := container.NewVBox(probabilityLabel, widget.NewSeparator()) // We create a new map with duplicates removed locusBasePairProbabilitiesMap_DuplicatesRemoved := make(map[string]float64) for basePair, basePairProbability := range locusBasePairProbabilitiesMap{ baseA, baseB, semicolonFound := strings.Cut(basePair, ";") if (semicolonFound == false) { return nil, errors.New("Invalid base pair found in locusBasePairProbabilitiesMap: " + basePair) } basePairDuplicate := baseB + ";" + baseA existingProbabilityValue, exists := locusBasePairProbabilitiesMap_DuplicatesRemoved[basePairDuplicate] if (exists == true){ // The duplicate has already been added. // We make sure the probability values match if (existingProbabilityValue != basePairProbability){ return nil, errors.New("locusBasePairProbabilitiesMap contains duplicate base pair with different value") } continue } locusBasePairProbabilitiesMap_DuplicatesRemoved[basePair] = basePairProbability } // All probabilities are mutually exclusive (you can only have 1 base pair for each genome locus) // Thus, we can add them together to get a total probability for each risk weight // Map structure: Risk Weight -> Probability of having weight riskWeightProbabilitiesMap := make(map[int]float64) for basePair, basePairProbability := range locusBasePairProbabilitiesMap_DuplicatesRemoved{ getBasePairRiskWeight := func()int{ basePairRiskWeight, exists := locusRiskWeightsMap[basePair] if (exists == false){ // This base pair has no known weight. We treat it as a 0 weight. return 0 } return basePairRiskWeight } basePairRiskWeight := getBasePairRiskWeight() riskWeightProbabilitiesMap[basePairRiskWeight] += basePairProbability } // Now we sort risk weights in order of least to greatest allRiskWeightsList := helpers.GetListOfMapKeys(riskWeightProbabilitiesMap) slices.Sort(allRiskWeightsList) for _, riskWeight := range allRiskWeightsList{ riskWeightProbability, exists := riskWeightProbabilitiesMap[riskWeight] if (exists == false){ return nil, errors.New("Risk weight probability not found in riskWeightProbabilitiesMap") } riskWeightString := helpers.ConvertIntToString(riskWeight) riskWeightPercentageProbability := riskWeightProbability * 100 riskWeightProbabilityString := helpers.ConvertFloat64ToStringRounded(riskWeightPercentageProbability, 2) riskWeightProbabilityFormatted := "~" + riskWeightProbabilityString + "%" riskWeightText := getBoldLabelCentered(riskWeightString) riskWeightProbabilityText := getBoldLabelCentered(riskWeightProbabilityFormatted) riskWeightColumn.Add(riskWeightText) riskWeightProbabilityColumn.Add(riskWeightProbabilityText) riskWeightColumn.Add(widget.NewSeparator()) riskWeightProbabilityColumn.Add(widget.NewSeparator()) } riskWeightHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseLocusRiskWeightExplainerPage(window, currentPage) }) riskWeightColumn.Add(riskWeightHelpButton) probabilityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setPolygenicDiseaseLocusRiskWeightProbabilityExplainerPage(window, currentPage) }) riskWeightProbabilityColumn.Add(probabilityHelpButton) basePairsGrid := container.NewHBox(layout.NewSpacer(), riskWeightColumn, riskWeightProbabilityColumn, layout.NewSpacer()) return basePairsGrid, nil } basePairsGrid, err := getBasePairsGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), diseaseNameRow, widget.NewSeparator(), locusNamesRow, widget.NewSeparator(), geneNameRow, widget.NewSeparator(), viewReferencesButton, widget.NewSeparator(), basePairsGrid) setPageContent(page, window) } func setViewGeneticAnalysisReferencesPage(window fyne.Window, referencesTopic string, referencesMap map[string]string, previousPage func()){ currentPage := func(){setViewGeneticAnalysisReferencesPage(window, referencesTopic, referencesMap, previousPage)} pageTitle := "Viewing " + referencesTopic + " References" title := getPageTitleCentered(pageTitle) backButton := getBackButtonCentered(previousPage) referencesContainer := container.NewVBox() referenceNamesList := helpers.GetListOfMapKeys(referencesMap) // We sort the references so they always show up in the same order slices.Sort(referenceNamesList) for index, referenceName := range referenceNamesList{ referenceLink, exists := referencesMap[referenceName] if (exists == false){ setErrorEncounteredPage(window, errors.New("referencesMap missing referenceName"), previousPage) return } indexString := helpers.ConvertIntToString(index+1) indexLabel := getBoldLabel(indexString + ".") referenceNameTrimmed, _, err := helpers.TrimAndFlattenString(referenceName, 35) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } referenceNameLabel := getItalicLabel(referenceNameTrimmed) referenceViewNameButton := widget.NewButtonWithIcon("View Name", theme.VisibilityIcon(), func(){ setViewTextPage(window, "Viewing Reference Name", referenceName, false, currentPage) }) referenceLinkButton := widget.NewButtonWithIcon("View Link", theme.VisibilityIcon(), func(){ setViewLinkPage(window, "Viewing Reference Link", referenceLink, currentPage) }) referenceRow := container.NewHBox(layout.NewSpacer(), indexLabel, referenceNameLabel, referenceViewNameButton, referenceLinkButton, layout.NewSpacer()) referencesContainer.Add(referenceRow) referencesContainer.Add(widget.NewSeparator()) } page := container.NewVBox(title, backButton, widget.NewSeparator(), referencesContainer) setPageContent(page, window) } func setViewTraitDetailsPage(window fyne.Window, traitName string, previousPage func()){ currentPage := func(){setViewTraitDetailsPage(window, traitName, previousPage)} title := getPageTitleCentered("Trait Details - " + traitName) backButton := getBackButtonCentered(previousPage) traitObject, err := traits.GetTraitObject(traitName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } traitDescription := traitObject.TraitDescription traitReferencesMap := traitObject.ReferencesMap traitNameLabel := widget.NewLabel("Trait Name:") traitNameText := getBoldLabel(traitName) traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, layout.NewSpacer()) traitDescriptionTrimmed, _, err := helpers.TrimAndFlattenString(traitDescription, 10) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } traitDescriptionLabel := widget.NewLabel("Description:") traitDescriptionText := getBoldLabel(traitDescriptionTrimmed) viewTraitDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewTextPage(window, "Trait Description", traitDescription, false, currentPage) }) traitDescriptionRow := container.NewHBox(layout.NewSpacer(), traitDescriptionLabel, traitDescriptionText, viewTraitDescriptionButton, layout.NewSpacer()) viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Trait", traitReferencesMap, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), traitDescriptionRow, widget.NewSeparator(), viewReferencesButton) setPageContent(page, window) } func setViewDiscreteTraitRuleDetailsPage(window fyne.Window, traitName string, ruleIdentifier string, previousPage func()){ currentPage := func(){setViewDiscreteTraitRuleDetailsPage(window, traitName, ruleIdentifier, previousPage)} title := getPageTitleCentered("Viewing Rule Details") backButton := getBackButtonCentered(previousPage) traitNameLabel := widget.NewLabel("Trait Name:") traitNameText := getBoldLabel(traitName) traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameLabel, traitNameText, layout.NewSpacer()) ruleIdentifierLabel := widget.NewLabel("Rule Identifier:") ruleIdentifierText := getBoldLabel(ruleIdentifier) ruleIdentifierRow := container.NewHBox(layout.NewSpacer(), ruleIdentifierLabel, ruleIdentifierText, layout.NewSpacer()) traitRuleObject, err := traits.GetTraitRuleObject(traitName, ruleIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } ruleOutcomePointsMap := traitRuleObject.OutcomePointsMap ruleReferencesMap := traitRuleObject.ReferencesMap viewReferencesButton := getWidgetCentered(widget.NewButtonWithIcon("View References", theme.ListIcon(), func(){ setViewGeneticAnalysisReferencesPage(window, "Rule", ruleReferencesMap, currentPage) })) ruleEffectsLabel := getBoldLabelCentered("Rule Effects:") getRuleEffectsGrid := func()(*fyne.Container, error){ outcomeNameLabel := getItalicLabelCentered("Outcome Name") outcomeEffectLabel := getItalicLabelCentered("Outcome Effect") outcomeNameColumn := container.NewVBox(outcomeNameLabel, widget.NewSeparator()) outcomeEffectColumn := container.NewVBox(outcomeEffectLabel, widget.NewSeparator()) for outcomeName, outcomePointsEffect := range ruleOutcomePointsMap{ outcomeNameLabel := getBoldLabelCentered(outcomeName) getOutcomeEffect := func()string{ outcomePointsEffectString := helpers.ConvertIntToString(outcomePointsEffect) if (outcomePointsEffect < 0){ return outcomePointsEffectString } outcomeEffect := "+" + outcomePointsEffectString return outcomeEffect } outcomeEffect := getOutcomeEffect() outcomeEffectLabel := getBoldLabelCentered(outcomeEffect) outcomeNameColumn.Add(outcomeNameLabel) outcomeEffectColumn.Add(outcomeEffectLabel) outcomeNameColumn.Add(widget.NewSeparator()) outcomeEffectColumn.Add(widget.NewSeparator()) } outcomeEffectHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setDiscreteTraitRuleOutcomeEffectsExplainerPage(window, currentPage) }) outcomeEffectColumn.Add(outcomeEffectHelpButton) ruleEffectsGrid := container.NewHBox(layout.NewSpacer(), outcomeNameColumn, outcomeEffectColumn, layout.NewSpacer()) return ruleEffectsGrid, nil } ruleEffectsGrid, err := getRuleEffectsGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), ruleIdentifierRow, widget.NewSeparator(), viewReferencesButton, widget.NewSeparator(), ruleEffectsLabel, ruleEffectsGrid) setPageContent(page, window) }