Пример #1
0
        public static void Section(ref Vector2 position, float width, Func <Vector2, float, float> drawerFunc, string header = null, int id = 0)
        {
            bool hasHeader = !header.NullOrEmpty();

            id = id != 0 ? id : drawerFunc.GetHashCode();

            // header
            if (hasHeader)
            {
                Rect headerRect = new Rect(position.x, position.y, width, SectionHeaderHeight);
                Widgets_Labels.Label(headerRect, header, TextAnchor.LowerLeft, GameFont.Tiny, margin: 3 * Margin);
                position.y += SectionHeaderHeight;
            }

            // draw content
            Rect contentRect = new Rect(
                position.x,
                position.y,
                width,
                GetHeight(id) + 2 * Margin);

            // NOTE: we're updating height _after_ drawing, so the background is technically always one frame behind.
            GUI.DrawTexture(contentRect, Resources.SlightlyDarkBackground);
            var height = drawerFunc(position + new Vector2(Margin, Margin), width - 2 * Margin);

            position.y  += height + 3 * Margin;
            _heights[id] = height;
        }
        public override void DrawListEntry(Rect rect, bool overview = true, bool active = true)
        {
            // (detailButton) | name | (bar | last update)/(stamp) -> handled in Utilities.DrawStatusForListEntry

            // set up rects
            Rect labelRect = new Rect(
                Margin, Margin, rect.width - (active ? StatusRectWidth + 4 * Margin : 2 * Margin),
                rect.height - 2 * Margin),
                 statusRect = new Rect(labelRect.xMax + Margin, Margin, StatusRectWidth,
                                       rect.height - 2 * Margin);

            // create label string
            string text = Label + "\n<i>";

            foreach (AgeAndSex ageSex in Utilities_Livestock.AgeSexArray)
            {
                text += Trigger.pawnKind.GetTame(manager, ageSex).Count + "/" + Trigger.CountTargets[ageSex] + ", ";
            }

            text += Trigger.pawnKind.GetWild(manager).Count + "</i>";
            string tooltip = Trigger.StatusTooltip;

            // do the drawing
            GUI.BeginGroup(rect);

            // draw label
            Widgets_Labels.Label(labelRect, text, tooltip);

            // if the bill has a manager job, give some more info.
            if (active)
            {
                this.DrawStatusForListEntry(statusRect, Trigger);
            }
            GUI.EndGroup();
        }
Пример #3
0
        public override void DrawTriggerConfig(ref Vector2 cur, float width, float entryHeight, string label = null, string tooltip = null, Action onClick = null)
        {
            // target threshold
            var thresholdLabelRect = new Rect(
                cur.x,
                cur.y,
                width,
                entryHeight);
            var searchIconRect = new Rect(
                thresholdLabelRect.xMax - Margin - entryHeight,
                cur.y,
                entryHeight,
                entryHeight);

            searchIconRect = searchIconRect.ContractedBy((searchIconRect.height - SmallIconSize) / 2);
            cur.y         += entryHeight;

            var thresholdRect = new Rect(
                cur.x,
                cur.y,
                width,
                SliderHeight);

            cur.y += entryHeight;

            var useResourceListerToggleRect = new Rect(
                cur.x,
                cur.y,
                width,
                entryHeight);

            cur.y += entryHeight;


            Widgets.DrawHighlightIfMouseover(thresholdLabelRect);
            if (label.NullOrEmpty())
            {
                label = "FMP.ThresholdCount".Translate(CurrentCount, TargetCount) + ":";
            }
            if (tooltip.NullOrEmpty())
            {
                tooltip = "FMP.ThresholdCountTooltip".Translate(CurrentCount, TargetCount);
            }

            Widgets_Labels.Label(thresholdLabelRect, label, tooltip);

            // add a little icon to mark interactivity
            GUI.DrawTexture(searchIconRect, Resources.Search);

            if (Widgets.ButtonInvisible(thresholdLabelRect))
            {
                onClick?.Invoke();
                Find.WindowStack.Add(DetailsWindow);
            }

            Utilities.DrawToggle(useResourceListerToggleRect, "FM.CountAllOnMap".Translate(), "FM.CountAllOnMap.Tip".Translate(), ref countAllOnMap, true);
            TargetCount = (int)GUI.HorizontalSlider(thresholdRect, TargetCount, 0, MaxUpperThreshold);
        }
Пример #4
0
        private void DrawOverview(Rect canvas)
        {
            // setup rects
            var legendRect = new Rect(canvas.xMin, canvas.yMin, (canvas.width - Margin) / 2f,
                                      canvas.height - ButtonSize.y - Margin);
            var plotRect = new Rect(legendRect.xMax + Margin, canvas.yMin,
                                    (canvas.width - Margin) / 2f, canvas.height);
            var buttonsRect = new Rect(canvas.xMin, legendRect.yMax + Margin,
                                       (canvas.width - Margin) / 2f, ButtonSize.y);

            // draw the plot
            overallHistory.DrawPlot(plotRect);

            // draw the detailed legend
            overallHistory.DrawDetailedLegend(legendRect, ref _overallScrollPos, null);

            // draw period switcher
            var periodRect = buttonsRect;

            periodRect.width /= 2f;

            // label
            Widgets_Labels.Label(periodRect, "FME.PeriodShown".Translate(tradingHistory.periodShown.ToString()),
                                 "FME.PeriodShownTooltip".Translate(tradingHistory.periodShown.ToString()));

            // mark interactivity
            var searchIconRect = periodRect;

            searchIconRect.xMin = searchIconRect.xMax - searchIconRect.height;
            if (searchIconRect.height > SmallIconSize)
            {
                // center it.
                searchIconRect = searchIconRect.ContractedBy((searchIconRect.height - SmallIconSize) / 2);
            }
            GUI.DrawTexture(searchIconRect, Resources.Search);
            Widgets.DrawHighlightIfMouseover(periodRect);
            if (Widgets.ButtonInvisible(periodRect))
            {
                var periodOptions = new List <FloatMenuOption>();
                for (var i = 0; i < History.periods.Length; i++)
                {
                    var period = History.periods[i];
                    periodOptions.Add(new FloatMenuOption(period.ToString(), delegate
                    {
                        tradingHistory.periodShown =
                            period;
                        overallHistory.periodShown =
                            period;
                    }));
                }

                Find.WindowStack.Add(new FloatMenu(periodOptions));
            }
        }
Пример #5
0
        private void DrawPawnOverviewRow(Pawn pawn, Rect rect)
        {
            // column width
            float colWidth = rect.width / 4 - Margin;

            // cell rects
            var nameRect     = new Rect(colWidth * 0, rect.yMin, colWidth, ListEntryHeight);
            var activityRect = new Rect(colWidth * 1, rect.yMin, colWidth * 2.5f, ListEntryHeight);
            var priorityRect = new Rect(colWidth * 3.5f, rect.yMin, colWidth * .5f, ListEntryHeight);

            // name
            Widgets.DrawHighlightIfMouseover(nameRect);

            // on click select and jump to location
            if (Widgets.ButtonInvisible(nameRect))
            {
                Find.MainTabsRoot.EscapeCurrentTab();
                CameraJumper.TryJump(pawn.PositionHeld, pawn.Map);
                Find.Selector.ClearSelection();
                if (pawn.Spawned)
                {
                    Find.Selector.Select(pawn);
                }
            }
            Widgets_Labels.Label(nameRect, pawn.Name.ToStringShort, "FM.ClickToJumpTo".Translate(pawn.LabelCap),
                                 TextAnchor.MiddleLeft, margin: Margin);

            // current activity (if curDriver != null)
            string activityString = pawn.jobs.curDriver?.GetReport() ?? "FM.NoCurJob".Translate();

            Widgets_Labels.Label(activityRect, activityString, pawn.jobs.curDriver?.GetReport(),
                                 TextAnchor.MiddleCenter, margin: Margin, font: GameFont.Tiny);

            // priority button
            Rect priorityPosition = new Rect(0f, 0f, 24f, 24f).CenteredOnXIn(priorityRect)
                                    .CenteredOnYIn(priorityRect);

            Text.Font = GameFont.Medium;
            WidgetsWork.DrawWorkBoxFor(priorityPosition.xMin, priorityPosition.yMin, pawn, WorkTypeDef, false);
            Text.Font = GameFont.Small;
        }
        public override void DrawListEntry(Rect rect, bool overview = true, bool active = true)
        {
            // (detailButton) | name | (bar | last update)/(stamp) -> handled in Utilities.DrawStatusForListEntry

            // set up rects
            Rect labelRect = new Rect(
                Margin,
                Margin,
                rect.width - (active ? StatusRectWidth + 4 * Margin : 2 * Margin),
                rect.height - 2 * Margin);
            Rect statusRect = new Rect(labelRect.xMax + Margin, Margin, StatusRectWidth, rect.height - 2 * Margin);

            // create label string
            string text    = Label + "\n";
            string subtext = string.Join(", ", Targets);

            if (subtext.Fits(labelRect))
            {
                text += subtext.Italic();
            }
            else
            {
                text += "multiple".Translate().Italic();
            }

            // do the drawing
            GUI.BeginGroup(rect);

            // draw label
            Widgets_Labels.Label(labelRect, text, subtext, TextAnchor.MiddleLeft, margin: Margin);

            // if the bill has a manager job, give some more info.
            if (active)
            {
                this.DrawStatusForListEntry(statusRect, Trigger);
            }
            GUI.EndGroup();
        }
Пример #7
0
        public void DrawPawnOverview(Rect rect)
        {
            // table body viewport
            var tableOutRect  = new Rect(0f, ListEntryHeight, rect.width, rect.height - ListEntryHeight);
            var tableViewRect = new Rect(0f, ListEntryHeight, rect.width, Workers.Count * ListEntryHeight);

            if (tableViewRect.height > tableOutRect.height)
            {
                tableViewRect.width -= ScrollbarWidth;
            }

            // column width
            float colWidth = tableViewRect.width / 4 - Margin;

            // column headers
            var nameColumnHeaderRect     = new Rect(colWidth * 0, 0f, colWidth, ListEntryHeight);
            var activityColumnHeaderRect = new Rect(colWidth * 1, 0f, colWidth * 2.5f, ListEntryHeight);
            var priorityColumnHeaderRect = new Rect(colWidth * 3.5f, 0f, colWidth * .5f, ListEntryHeight);

            // label for priority column
            string workLabel = Find.PlaySettings.useWorkPriorities
                                   ? "FM.Priority".Translate()
                                   : "FM.Enabled".Translate();

            // begin drawing
            GUI.BeginGroup(rect);

            // draw labels
            Widgets_Labels.Label(nameColumnHeaderRect, WorkTypeDef.pawnLabel + "FM.PluralSuffix".Translate(), TextAnchor.LowerCenter);
            Widgets_Labels.Label(activityColumnHeaderRect, "FM.Activity".Translate(), TextAnchor.LowerCenter);
            Widgets_Labels.Label(priorityColumnHeaderRect, workLabel, TextAnchor.LowerCenter);

            // begin scrolling area
            Widgets.BeginScrollView(tableOutRect, ref _workersScrollPosition, tableViewRect);
            GUI.BeginGroup(tableViewRect);

            // draw pawn rows
            Vector2 cur = Vector2.zero;

            for (var i = 0; i < Workers.Count; i++)
            {
                var row = new Rect(cur.x, cur.y, tableViewRect.width, ListEntryHeight);
                if (i % 2 == 0)
                {
                    Widgets.DrawAltRect(row);
                }
                try
                {
                    DrawPawnOverviewRow(Workers[i], row);
                }
                catch // pawn death, etc.
                {
                    // rehresh the list and skip drawing untill the next GUI tick.
                    RefreshWorkers();
                    Widgets.EndScrollView();
                    return;
                }

                cur.y += ListEntryHeight;
            }

            // end scrolling area
            GUI.EndGroup();
            Widgets.EndScrollView();

            // done!
            GUI.EndGroup();
        }
        public override void DrawTriggerConfig(ref Vector2 cur, float width, float entryHeight, string label = null, string tooltip = null, List <Designation> designations = null, Action onOpenFilterDetails = null, Func <Designation, string> designationLabelGetter = null)
        {
            var hasTargets = !designations.NullOrEmpty();

            // target threshold
            var thresholdLabelRect = new Rect(
                cur.x,
                cur.y,
                width - (hasTargets ? SmallIconSize + Margin * 2 : 0f),
                entryHeight);
            var detailsWindowButtonRect = new Rect(
                thresholdLabelRect.xMax - SmallIconSize - Margin,
                cur.y + (entryHeight - SmallIconSize) / 2f,
                SmallIconSize,
                SmallIconSize);
            var targetsButtonRect = new Rect(
                thresholdLabelRect.xMax + Margin,
                cur.y + (entryHeight - SmallIconSize) / 2f,
                SmallIconSize,
                SmallIconSize
                );

            cur.y += entryHeight;

            var thresholdRect = new Rect(
                cur.x,
                cur.y,
                width,
                SliderHeight);

            cur.y += entryHeight;

            var useResourceListerToggleRect = new Rect(
                cur.x,
                cur.y,
                width,
                entryHeight);

            cur.y += entryHeight;


            Widgets.DrawHighlightIfMouseover(thresholdLabelRect);
            if (label.NullOrEmpty())
            {
                label = "FMP.ThresholdCount".Translate(CurrentCount, TargetCount) + ":";
            }
            if (tooltip.NullOrEmpty())
            {
                tooltip = "FMP.ThresholdCountTooltip".Translate(CurrentCount, TargetCount);
            }

            Widgets_Labels.Label(thresholdLabelRect, label, tooltip);

            // add a little icon to mark interactivity
            GUI.color = Mouse.IsOver(thresholdLabelRect) ? GenUI.MouseoverColor : Color.white;
            GUI.DrawTexture(detailsWindowButtonRect, Resources.Cog);
            GUI.color = Color.white;
            if (Widgets.ButtonInvisible(thresholdLabelRect))
            {
                onOpenFilterDetails?.Invoke();
                Find.WindowStack.Add(DetailsWindow);
            }

            // target list
            if (hasTargets)
            {
                if (Widgets.ButtonImage(targetsButtonRect, Resources.Search))
                {
                    List <FloatMenuOption> options = new List <FloatMenuOption>();
                    foreach (var designation in designations)
                    {
                        string option  = string.Empty;
                        Action onClick = () => Find.WindowStack.TryRemove(typeof(MainTabWindow_Manager), false);
                        Action onHover = null;
                        if (designation.target.HasThing)
                        {
                            var thing = designation.target.Thing;
                            option   = designationLabelGetter?.Invoke(designation) ?? thing.LabelCap;
                            onClick += () => CameraJumper.TryJumpAndSelect(thing);
                            onHover += () => CameraJumper.TryJump(thing);
                        }
                        else
                        {
                            var cell = designation.target.Cell;
                            var map  = Find.CurrentMap;
                            // designation.map would be better, but that's private. We should only ever be looking at jobs on the current map anyway,
                            // so I suppose it doesn't matter -- Fluffy.
                            option   = designationLabelGetter?.Invoke(designation) ?? cell.GetTerrain(map).LabelCap;
                            onClick += () => CameraJumper.TryJump(cell, map);
                            onHover += () => CameraJumper.TryJump(cell, map);
                        }
                        options.Add(new FloatMenuOption(option, onClick, MenuOptionPriority.Default, onHover));
                    }
                    Find.WindowStack.Add(new FloatMenu(options));
                }
            }

            Utilities.DrawToggle(useResourceListerToggleRect, "FM.CountAllOnMap".Translate(), "FM.CountAllOnMap.Tip".Translate(), ref countAllOnMap, true);
            TargetCount = (int)GUI.HorizontalSlider(thresholdRect, TargetCount, 0, MaxUpperThreshold);
        }
Пример #9
0
        public void DrawDetailedLegend(Rect canvas, ref Vector2 scrollPos, int?max, bool positiveOnly = false,
                                       bool negativeOnly = false)
        {
            // set sign
            int sign = negativeOnly ? -1 : 1;

            List <Chapter> ChaptersOrdered = _chapters
                                             .Where(chapter => !positiveOnly || chapter.pages[periodShown].Any(i => i > 0))
                                             .Where(chapter => !negativeOnly || chapter.pages[periodShown].Any(i => i < 0))
                                             .OrderByDescending(chapter => chapter.Last(periodShown) * sign).ToList();

            // get out early if no chapters.
            if (ChaptersOrdered.Count == 0)
            {
                GUI.DrawTexture(canvas.ContractedBy(Margin), Resources.SlightlyDarkBackground);
                Widgets_Labels.Label(canvas, "FM.HistoryNoChapters".Translate(), TextAnchor.MiddleCenter, color: Color.grey);
                return;
            }

            // max
            float _max = max ?? (DrawMaxMarkers
                                      ? ChaptersOrdered.Max(chapter => chapter.TrueMax)
                                      : ChaptersOrdered.FirstOrDefault()?.Last(periodShown) * sign)
                         ?? 0;

            // cell height
            var height    = 30f;
            var barHeight = 18f;

            // n rows
            int n = ChaptersOrdered.Count;

            // scrolling region
            Rect viewRect = canvas;

            viewRect.height = n * height;
            if (viewRect.height > canvas.height)
            {
                viewRect.width -= 16f + Margin;
                canvas.width   -= Margin;
                canvas.height  -= 1f;
            }
            Widgets.BeginScrollView(canvas, ref scrollPos, viewRect);
            for (var i = 0; i < n; i++)
            {
                // set up rects
                var  row  = new Rect(0f, height * i, viewRect.width, height);
                Rect icon = new Rect(Margin, height * i, height, height).ContractedBy(Margin / 2f);
                // icon is square, size defined by height.
                var bar = new Rect(Margin + height, height * i, viewRect.width - height - Margin,
                                   height);

                // if icons should not be drawn make the bar full size.
                if (!DrawIcons)
                {
                    bar.xMin -= height + Margin;
                }

                // bar details.
                Rect  barBox   = bar.ContractedBy((height - barHeight) / 2f);
                Rect  barFill  = barBox.ContractedBy(2f);
                float maxWidth = barFill.width;
                if (MaxPerChapter)
                {
                    barFill.width *= ChaptersOrdered[i].Last(periodShown) * sign / (float)ChaptersOrdered[i].TrueMax;
                }
                else
                {
                    barFill.width *= ChaptersOrdered[i].Last(periodShown) * sign / _max;
                }

                GUI.BeginGroup(viewRect);

                // if DrawIcons and a thing is set, draw the icon.
                ThingDef thing = ChaptersOrdered[i].ThingDefCount.thingDef;
                if (DrawIcons && thing != null)
                {
                    // draw the icon in correct proportions
                    float proportion = GenUI.IconDrawScale(thing);
                    Widgets.DrawTextureFitted(icon, thing.uiIcon, proportion);

                    // draw counts in upper left corner
                    if (DrawCounts)
                    {
                        Utilities.LabelOutline(icon, ChaptersOrdered[i].ThingDefCount.count.ToString(), null,
                                               TextAnchor.UpperLeft, 0f, GameFont.Tiny, Color.white, Color.black);
                    }
                }

                // if desired, draw ghost bar
                if (DrawMaxMarkers)
                {
                    Rect ghostBarFill = barFill;
                    ghostBarFill.width = MaxPerChapter ? maxWidth : maxWidth * (ChaptersOrdered[i].TrueMax / _max);
                    GUI.color          = new Color(1f, 1f, 1f, .2f);
                    GUI.DrawTexture(ghostBarFill, ChaptersOrdered[i].Texture);   // coloured texture
                    GUI.color = Color.white;
                }

                // draw the main bar.
                GUI.DrawTexture(barBox, Resources.SlightlyDarkBackground);
                GUI.DrawTexture(barFill, ChaptersOrdered[i].Texture); // coloured texture
                GUI.DrawTexture(barFill, Resources.BarShader);        // slightly fancy overlay (emboss).

                // draw on bar info
                if (DrawInfoInBar)
                {
                    string info = ChaptersOrdered[i].label + ": " +
                                  FormatCount(ChaptersOrdered[i].Last(periodShown) * sign);

                    if (DrawMaxMarkers)
                    {
                        info += " / " + FormatCount(ChaptersOrdered[i].TrueMax);
                    }

                    // offset label a bit downwards and to the right
                    Rect rowInfoRect = row;
                    rowInfoRect.y += 3f;
                    rowInfoRect.x += Constants.Margin * 2;

                    // x offset
                    float xOffset = DrawIcons && thing != null ? height + Margin * 2 : Margin * 2;

                    Utilities.LabelOutline(rowInfoRect, info, null, TextAnchor.MiddleLeft, xOffset, GameFont.Tiny,
                                           Color.white, Color.black);
                }

                // are we currently showing this line?
                bool shown = _chaptersShown.Contains(ChaptersOrdered[i]);

                // tooltip on entire row
                string tooltip = ChaptersOrdered[i].label + ": " +
                                 FormatCount(Mathf.Abs(ChaptersOrdered[i].Last(periodShown)));
                tooltip += "FM.HistoryClickToEnable".Translate(shown ? "hide" : "show", ChaptersOrdered[i].label);
                TooltipHandler.TipRegion(row, tooltip);

                // handle input
                if (Widgets.ButtonInvisible(row))
                {
                    if (Event.current.button == 0)
                    {
                        if (shown)
                        {
                            _chaptersShown.Remove(ChaptersOrdered[i]);
                        }
                        else
                        {
                            _chaptersShown.Add(ChaptersOrdered[i]);
                        }
                    }
                    else if (Event.current.button == 1)
                    {
                        _chaptersShown.Clear();
                        _chaptersShown.Add(ChaptersOrdered[i]);
                    }
                }

                // UI feedback for disabled row
                if (!shown)
                {
                    GUI.DrawTexture(row, Resources.SlightlyDarkBackground);
                }

                GUI.EndGroup();
            }

            Widgets.EndScrollView();
        }
Пример #10
0
        public void DrawPlot(Rect rect, int target = 0, string label = "", bool positiveOnly = false,
                             bool negativeOnly     = false)
        {
            // set sign
            int sign = negativeOnly ? -1 : 1;

            // subset chapters
            List <Chapter> chapters =
                _chaptersShown.Where(chapter => !positiveOnly || chapter.pages[periodShown].Any(i => i > 0))
                .Where(chapter => !negativeOnly || chapter.pages[periodShown].Any(i => i < 0))
                .ToList();

            // get out early if no chapters.
            if (chapters.Count == 0)
            {
                GUI.DrawTexture(rect.ContractedBy(Margin), Resources.SlightlyDarkBackground);
                Widgets_Labels.Label(rect, "FM.HistoryNoChapters".Translate(), TextAnchor.MiddleCenter, color: Color.grey);
                return;
            }

            // stuff we need
            Rect plot = rect.ContractedBy(Margin);

            plot.xMin += _yAxisMargin;

            // maximum of all chapters.
            int max =
                CeilToPrecision(Math.Max(chapters.Select(c => c.Max(periodShown, !negativeOnly)).Max(), target) *
                                1.2f);

            // size, and pixels per node.
            float w  = plot.width;
            float h  = plot.height;
            float wu = w / Size;           // width per section
            float hu = h / max;            // height per count
            int   bi = max / (Breaks + 1); // count per break
            float bu = hu * bi;            // height per break

            // plot the line(s)
            GUI.DrawTexture(plot, Resources.SlightlyDarkBackground);
            GUI.BeginGroup(plot);
            foreach (Chapter chapter in chapters)
            {
                chapter.Plot(periodShown, plot.AtZero(), wu, hu, sign);
            }

            // handle mouseover events
            if (Mouse.IsOver(plot.AtZero()))
            {
                // very conveniently this is the position within the current group.
                Vector2 pos  = Event.current.mousePosition;
                var     upos = new Vector2(pos.x / wu, (plot.height - pos.y) / hu);

                // get distances
                float[] distances =
                    chapters.Select(c => Math.Abs(c.ValueAt(periodShown, (int)upos.x, sign) - upos.y)).ToArray();

                // get the minimum index
                float min      = int.MaxValue;
                var   minIndex = 0;
                for (var i = 0; i < distances.Count(); i++)
                {
                    if (distances[i] < min)
                    {
                        minIndex = i;
                        min      = distances[i];
                    }
                }

                // closest line
                Chapter closest = chapters[minIndex];

                // do minimum stuff.
                var realpos  = new Vector2(pos.x, plot.height - closest.ValueAt(periodShown, (int)upos.x, sign) * hu);
                var blipRect = new Rect(realpos.x - SmallIconSize / 2f,
                                        realpos.y - SmallIconSize / 2f, SmallIconSize,
                                        SmallIconSize);
                GUI.color = closest.lineColor;
                GUI.DrawTexture(blipRect, Resources.StageB);
                GUI.color = DefaultLineColor;

                // get orientation of tooltip
                Vector2 tippos = realpos + new Vector2(Margin, Margin);
                string  tip    = chapters[minIndex].label + ": " +
                                 FormatCount(chapters[minIndex].ValueAt(periodShown, (int)upos.x, sign));
                Vector2 tipsize = Text.CalcSize(tip);
                bool    up = false, left = false;
                if (tippos.x + tipsize.x > plot.width)
                {
                    left      = true;
                    tippos.x -= tipsize.x + 2 * +Margin;
                }
                if (tippos.y + tipsize.y > plot.height)
                {
                    up        = true;
                    tippos.y -= tipsize.y + 2 * Margin;
                }

                var anchor = TextAnchor.UpperLeft;
                if (up && left)
                {
                    anchor = TextAnchor.LowerRight;
                }
                if (up && !left)
                {
                    anchor = TextAnchor.LowerLeft;
                }
                if (!up && left)
                {
                    anchor = TextAnchor.UpperRight;
                }
                var tooltipRect = new Rect(tippos.x, tippos.y, tipsize.x, tipsize.y);
                Widgets_Labels.Label(tooltipRect, tip, anchor: anchor, font: GameFont.Tiny);
            }

            // draw target line
            if (DrawTargetLine)
            {
                GUI.color = Color.gray;
                for (var i = 0; i < plot.width / DashLength; i += 2)
                {
                    Widgets.DrawLineHorizontal(i * DashLength, plot.height - target * hu, DashLength);
                }
            }

            // draw legend
            int lineCount = _chapters.Count;

            if (AllowTogglingLegend && lineCount > 1 && DrawInlineLegend)
            {
                var rowHeight  = 20f;
                var lineLength = 30f;
                var labelWidth = 100f;

                Vector2 cur = Vector2.zero;
                foreach (Chapter chapter in _chapters)
                {
                    GUI.color = chapter.lineColor;
                    Widgets.DrawLineHorizontal(cur.x, cur.y + rowHeight / 2f, lineLength);
                    cur.x += lineLength;
                    Widgets_Labels.Label(ref cur, labelWidth, rowHeight, chapter.label, font: GameFont.Tiny);
                    cur.x = 0f;
                }

                GUI.color = Color.white;
            }

            GUI.EndGroup();

            // plot axis
            GUI.BeginGroup(rect);
            Text.Anchor = TextAnchor.MiddleRight;
            Text.Font   = GameFont.Tiny;

            // draw ticks + labels
            for (var i = 1; i < Breaks + 1; i++)
            {
                Widgets.DrawLineHorizontal(_yAxisMargin + Margin / 2, plot.height - i * bu, Margin);
                var labRect = new Rect(0f, plot.height - i * bu - 4f, _yAxisMargin, 20f);
                Widgets.Label(labRect, FormatCount(i * bi));
            }

            Text.Font   = GameFont.Small;
            Text.Anchor = TextAnchor.UpperLeft;
            GUI.color   = Color.white;

            rect = rect.AtZero(); // ugh, I'm tired, just work.

            // period / variables picker
            if (DrawOptions)
            {
                var switchRect = new Rect(rect.xMax - SmallIconSize - Margin,
                                          rect.yMin + Margin, SmallIconSize,
                                          SmallIconSize);

                Widgets.DrawHighlightIfMouseover(switchRect);
                if (Widgets.ButtonImage(switchRect, Resources.Cog))
                {
                    List <FloatMenuOption> options =
                        periods.Select(
                            p =>
                            new FloatMenuOption("FM.HistoryPeriod".Translate() + ": " + p.ToString(),
                                                delegate
                                                { periodShown = p; })).ToList();
                    if (AllowTogglingLegend && _chapters.Count > 1)   // add option to show/hide legend if appropriate.
                    {
                        options.Add(new FloatMenuOption("FM.HistoryShowHideLegend".Translate(),
                                                        delegate
                                                        { DrawInlineLegend = !DrawInlineLegend; }));
                    }
                    Find.WindowStack.Add(new FloatMenu(options));
                }
            }

            GUI.EndGroup();
        }