Пример #1
0
        // Given a SpecificLayout, sets any necessary properties to make it suitable to return to the caller of GetBestLayout(LayoutQuery)
        protected SpecificLayout prepareLayoutForQuery(SpecificLayout layout, LayoutQuery query)
        {
            if (layout != null)
            {
                if (layout.Width < 0 || layout.Height < 0)
                {
                    ErrorReporter.ReportParadox("Illegal layout size: " + layout.Size);
                    this.GetBestLayout(query);
                }
            }
            int numMatches;

            //if (query.Debug)
            {
                if (query.ProposedSolution_ForDebugging != null)
                {
                    if (!query.Accepts(query.ProposedSolution_ForDebugging))
                    {
                        ErrorReporter.ReportParadox("Error: the proposed solution was not valid");
                    }
                }
                if (layout != null && !query.Accepts(layout))
                {
                    ErrorReporter.ReportParadox("Error: the returned layout was not valid");
                    LayoutQuery query2 = query.DebugClone();
                    this.GetBestLayout(query2);
                }
            }

            if (layout != null)
            {
                //layout.Set_SourceParent(this);

                numMatches = 0;

                /*foreach (LayoutChoice_Set ancestor in layout.GetAncestors())
                 * {
                 *  if (ancestor == this)
                 *      numMatches++;
                 * }
                 * if (numMatches == 0)
                 *  ErrorReporter.ReportParadox("Error: the returned layout did not come from this layout");
                 * if (numMatches > 1)
                 *  ErrorReporter.ReportParadox("Error: the returned layout contained multiple ancestors matching this one");
                 */
                layout.SourceQuery = query;
            }

            if (this.parents.Count < 1 && !(this is ViewManager))
            {
                throw new InvalidOperationException("No parents assigned to " + this);
            }

            query.OnAnswered(this);

            return(layout);
        }
Пример #2
0
        private SpecificLayout MakeLayout(double width, double height, LayoutQuery layoutQuery)
        {
            SpecificLayout layout = this.prepareLayoutForQuery(new Specific_LeafLayout(this.view, new LayoutDimensions(width, height, this.ComputeScore(width, height))), layoutQuery);

            if (!layoutQuery.Accepts(layout))
            {
                ErrorReporter.ReportParadox("Error; ImageLayout attempted to return an invalid layout result");
            }
            return(layout);
        }
Пример #3
0
        // computes the size of the layout with smallest height satisfying the given criteria
        private SpecificLayout Get_MinHeight_Layout(LayoutQuery query)
        {
            if (query.MaxWidth < 0 || query.MaxHeight < 0)
            {
                return(null);
            }
            // first check whether this query will accept a cropped layout
            Specific_TextLayout specificLayout = this.ComputeDimensions(new Size(0, 0), query.Debug);

            if (query.Accepts(specificLayout))
            {
                return(this.prepareLayoutForQuery(specificLayout, query));
            }
            specificLayout = this.ComputeDimensions(new Size(query.MaxWidth, query.MaxHeight), query.Debug);
            if (query.Accepts(specificLayout))
            {
                return(this.prepareLayoutForQuery(specificLayout.GetBestLayout(query), query));
            }
            return(null);
        }
Пример #4
0
        // computes the size of the highest-scoring layout satisfying the given criteria
        private SpecificLayout Get_MaxScoring_Layout(LayoutQuery query)
        {
            if (query.MaxWidth < 0 || query.MaxHeight < 0)
            {
                return(null);
            }
            Specific_TextLayout specificLayout = this.ComputeDimensions(new Size(query.MaxWidth, query.MaxHeight), query.Debug);

            if (query.Accepts(specificLayout))
            {
                return(this.prepareLayoutForQuery(specificLayout, query));
            }
            return(null);
        }
Пример #5
0
        public override SpecificLayout GetBestLayout(LayoutQuery query)
        {
            Specific_ContainerLayout result;

            SpecificLayout sublayoutResult;

            if (this.SubLayout != null)
            {
                // We have a sublayout and haven't been asked to wrap it in another view, so we can just forward the query on to it
                sublayoutResult = this.SubLayout.GetBestLayout(query);
                if (this.view == null)
                {
                    // If we haven't been asked to wrap the sublayout's result, we can just directly use it
                    return(sublayoutResult);
                }
                else
                {
                    if (sublayoutResult == null)
                    {
                        return(null);
                    }
                    result = this.makeSpecificLayout(this.view, sublayoutResult.Size, LayoutScore.Zero, sublayoutResult, new Thickness());
                    this.prepareLayoutForQuery(result, query);
                    return(result);
                }
            }

            // if there is no subLayout, for now we just return an empty size
            Specific_ContainerLayout empty = this.makeSpecificLayout(this.view, new Size(), LayoutScore.Zero, null, new Thickness());

            if (query.Accepts(empty))
            {
                result = empty;
            }
            else
            {
                result = null;
            }
            this.prepareLayoutForQuery(result, query);
            return(result);
        }
Пример #6
0
        public override SpecificLayout GetBestLayout(LayoutQuery query)
        {
            Specific_ContainerLayout result;

            // Determine whether there's room for the border
            double      borderWidth  = this.BorderThickness.Left + this.BorderThickness.Right;
            double      borderHeight = this.BorderThickness.Top + this.BorderThickness.Bottom;
            LayoutQuery subQuery     = query.WithDimensions(query.MaxWidth - borderWidth, query.MaxHeight - borderHeight);

            if (subQuery.MaxWidth < 0 || subQuery.MaxHeight < 0)
            {
                return(null);
            }

            // Query sublayout if it exists
            if (this.SubLayout != null)
            {
                SpecificLayout best_subLayout = this.SubLayout.GetBestLayout(subQuery);
                if (best_subLayout != null)
                {
                    result = this.makeSpecificLayout(this.view, new Size(best_subLayout.Width + borderWidth, best_subLayout.Height + borderHeight), LayoutScore.Zero, best_subLayout, this.BorderThickness);
                    result.ChildFillsAvailableSpace = this.ChildFillsAvailableSpace;
                    this.prepareLayoutForQuery(result, query);
                    return(result);
                }
                return(null);
            }
            // if there is no subLayout, for now we just return an empty size
            Specific_ContainerLayout empty = this.makeSpecificLayout(this.view, new Size(), LayoutScore.Zero, null, new Thickness());

            if (query.Accepts(empty))
            {
                result = empty;
            }
            else
            {
                result = null;
            }
            this.prepareLayoutForQuery(result, query);
            return(result);
        }
Пример #7
0
        // computes the size of the layout with smallest width satisfying the given criteria
        private SpecificLayout Get_MinWidth_Layout(LayoutQuery query)
        {
            if (query.MaxWidth < 0 || query.MaxHeight < 0)
            {
                return(null);
            }
            // first check whether this query will accept a cropped layout
            Specific_TextLayout specificLayout = this.ComputeDimensions(new Size(0, 0), query.Debug);

            if (query.Accepts(specificLayout))
            {
                return(this.prepareLayoutForQuery(specificLayout, query));
            }
            // not satisfied with cropping so we need to try harder to do a nice-looking layout
            Specific_TextLayout nonCropping_layout = this.Get_NonCropping_MinWidthLayout(query);

            if (nonCropping_layout != null)
            {
                return(this.prepareLayoutForQuery(nonCropping_layout, query));
            }
            return(null);
        }
Пример #8
0
        // computes the layout dimensions of the layout of minimum width such that there is no cropping
        private Specific_TextLayout Get_NonCropping_MinWidthLayout(LayoutQuery query)
        {
            Specific_TextLayout bestAllowedDimensions = this.ComputeDimensions(new Size(double.PositiveInfinity, double.PositiveInfinity), query.Debug);
            double pixelSize        = 1;
            double maxRejectedWidth = 0;
            int    numIterations    = 0;
            bool   firstIteration   = true;
            double maxWidth         = query.MaxWidth;

            while (maxRejectedWidth < bestAllowedDimensions.Width - pixelSize / 2)
            {
                numIterations++;
                // given the current width, compute the required height
                Specific_TextLayout newDimensions = this.ComputeDimensions(new Size(maxWidth, double.PositiveInfinity), query.Debug);
                if (newDimensions.Height <= query.MaxHeight && query.MinScore.CompareTo(newDimensions.Score) <= 0)
                {
                    // this layout fits in the required dimensions
                    if (newDimensions.Width <= bestAllowedDimensions.Width && newDimensions.Width <= query.MaxWidth)
                    {
                        // this layout is at least as good as the best layout we found so far
                        bestAllowedDimensions = newDimensions;
                        maxWidth = newDimensions.Width;
                    }
                    else
                    {
                        // we've found a layout having sufficiently small width and height, but it isn't any better than what we'd previously found
                        // So, we're not making progress with this process and should quit
                        break;
                    }
                }
                else
                {
                    // this layout does not fit in the required dimensions
                    if (maxWidth > maxRejectedWidth)
                    {
                        maxRejectedWidth = maxWidth;
                    }
                    // if the first layout we found was too tall, then there will need to be some cropping
                    if (double.IsPositiveInfinity(bestAllowedDimensions.Width))
                    {
                        return(null);
                    }
                }
                // calculate a new size, by guessing based on required area
                double desiredArea = newDimensions.Height * Math.Max(maxWidth, newDimensions.Width);
                maxWidth = desiredArea / query.MaxHeight;
                // Make sure that the next value we check is inside the range that we haven't checked yet, to make sure we're making progress
                // If our area-based is outside the unexplored range, then from now on just split the remaining range in half on each iteration
                if (maxWidth < (maxRejectedWidth + pixelSize / 2))
                {
                    if (firstIteration)
                    {
                        // The first time that we find we have enough area to make the width very tiny, we calculate the true minimum amount of width required
                        Size desiredSize = this.formatText(maxWidth, query.Debug, this.FontSize).Size;
                        if (desiredSize.Width > maxWidth)
                        {
                            maxRejectedWidth = desiredSize.Width - pixelSize / 2;
                        }
                        maxWidth = desiredSize.Width;
                    }
                    else
                    {
                        // The second time we find that we have enough area to make the width very tiny, we don't recalculate the true min width required because we already did
                        // Instead we just do a binary search
                        maxWidth = (maxRejectedWidth + bestAllowedDimensions.Width) / 2;
                    }
                }
                else
                {
                    if (maxWidth > (bestAllowedDimensions.Width - pixelSize / 2))
                    {
                        maxWidth = (maxRejectedWidth + bestAllowedDimensions.Width) / 2;
                    }
                }
                firstIteration = false;
            }
            if (this.LoggingEnabled)
            {
                System.Diagnostics.Debug.WriteLine("Spent " + numIterations + " iterations in Get_NonCropping_MinWidthLayout with query = " + query + " and text length = " + this.TextLength);
            }
            if (!query.Accepts(bestAllowedDimensions))
            {
                return(null);
            }
            return(bestAllowedDimensions);
        }
Пример #9
0
        public override SpecificLayout GetBestLayout(LayoutQuery query)
        {
            // The score of a Specific_ScrollLayout is defined in returnLayout:
            // If the child layout has negative score, the Specific_ScrollLayout refuses to do a layout
            // If the child layout has nonnegative score, the Specific_ScrollLayout's score equals
            //   (this.resultingScore * (what fraction of the child layout is visible))
            if (query.MinScore.CompareTo(this.resultingScore) > 0)
            {
                // Demands too high of a score: no solution
                return(null);
            }

            if (query.MaxHeight <= 0)
            {
                return(null);
            }

            // what fraction of the score of the sublayout will appear onscreen at once
            double scoreFraction = Math.Max(query.MinScore.DividedBy(this.resultingScore), 0);
            // what fraction of the child's height we need to include in the size of the ScrollView
            double requiredHeightFraction = scoreFraction;
            // the child's height divided by the ScrollView's height
            double requiredHeightMultiplier;

            if (requiredHeightFraction != 0)
            {
                requiredHeightMultiplier = 1 / requiredHeightFraction;
            }
            else
            {
                requiredHeightMultiplier = double.PositiveInfinity;
            }
            // the maximum child height
            double maxChildHeight = query.MaxHeight * requiredHeightMultiplier;

            if (query.MinimizesWidth())
            {
                // For a min-width query, first shrink the width as much as possible before continuing
                SpecificLayout minWidth_childLayout = this.subLayout.GetBestLayout(new MinWidth_LayoutQuery(query.MaxWidth, maxChildHeight, this.requiredChildScore));
                if (minWidth_childLayout == null)
                {
                    return(null);
                }
                query = query.WithDimensions(minWidth_childLayout.Width, minWidth_childLayout.Height);
            }

            SpecificLayout childLayout = this.subLayout.GetBestLayout(new MinHeight_LayoutQuery(query.MaxWidth, maxChildHeight, this.requiredChildScore));

            if (childLayout == null)
            {
                return(null);
            }

            if (!query.MinimizesHeight())
            {
                // For a max-score (or min-width) query, use as much height as was allowed
                Size           size   = new Size(childLayout.Width, Math.Min(query.MaxHeight, childLayout.Height));
                SpecificLayout result = this.makeLayout(size, childLayout);
                if (query.Accepts(result))
                {
                    return(this.prepareLayoutForQuery(result, query));
                }
                return(null);
            }
            else
            {
                // For a min-height query, use only as much size as is needed
                double requiredScrollviewHeight = childLayout.Height * requiredHeightFraction;
                Size   size = new Size(childLayout.Width, requiredScrollviewHeight);

                SpecificLayout result = this.makeLayout(size, childLayout);
                if (!query.Accepts(result))
                {
                    // Check for possible rounding error
                    SpecificLayout larger = this.makeLayout(new Size(size.Width, size.Height + this.pixelSize), childLayout);
                    if (query.Accepts(larger))
                    {
                        return(this.prepareLayoutForQuery(larger, query));
                    }
                    return(null);
                }
                return(this.prepareLayoutForQuery(result, query));
            }
        }