-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
479 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Copyright 2018 Google Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package text | ||
|
||
// scroll.go contains code that tracks the current scrolling position. | ||
|
||
import "math" | ||
|
||
// scrollTracker tracks the current scrolling position for the Text widget. | ||
// This is not thread safe. | ||
type scrollTracker struct { | ||
// scroll stores user requests to scroll up (negative) or down (positive). | ||
// E.g. -1 means up by one line and 2 means down by two lines. | ||
scroll int | ||
|
||
// scrollPage stores user requests to scroll up (negative) or down | ||
// (positive) by a page of content. E.g. -1 means up by one page and 2 | ||
// means down by two pages. | ||
scrollPage int | ||
|
||
// first tracks the first line that will be printed. | ||
first int | ||
|
||
// state is the state of the scrolling FSM. | ||
state rollState | ||
} | ||
|
||
// newScrollTracker returns a new scroll tracker. | ||
func newScrollTracker(opts *options) *scrollTracker { | ||
if opts.rollContent { | ||
return &scrollTracker{state: rollToEnd} | ||
} | ||
return &scrollTracker{state: rollingDisabled} | ||
} | ||
|
||
// upOneLine processes a user request to scroll up by one line. | ||
func (st *scrollTracker) upOneLine() { | ||
st.scroll-- | ||
} | ||
|
||
// downOneLine processes a user request to scroll down by one line. | ||
func (st *scrollTracker) downOneLine() { | ||
st.scroll++ | ||
} | ||
|
||
// upOnePage processes a user request to scroll up by one page. | ||
func (st *scrollTracker) upOnePage() { | ||
st.scrollPage-- | ||
} | ||
|
||
// downOnePage processes a user request to scroll down by one page. | ||
func (st *scrollTracker) downOnePage() { | ||
st.scrollPage++ | ||
} | ||
|
||
// doScroll processes any outstanding scroll requests and calculates the | ||
// resulting first line. | ||
func (st *scrollTracker) doScroll(lines, height int) int { | ||
first := st.first + st.scroll + st.scrollPage*height | ||
st.scroll = 0 | ||
st.scrollPage = 0 | ||
return normalizeScroll(first, lines, height) | ||
} | ||
|
||
// firstLine returns the number of the first line that should be drawn on a | ||
// canvas of the specified height if there is the provided number of lines of | ||
// text. | ||
func (st *scrollTracker) firstLine(lines, height int) int { | ||
// Execute the scrolling FSM. | ||
st.state = st.state(st, lines, height) | ||
return st.first | ||
} | ||
|
||
// rollState is a state in the scrolling FSM. | ||
type rollState func(st *scrollTracker, lines, height int) rollState | ||
|
||
// rollingDisabled is a state where content rolling was disabled by the | ||
// configuration of the Text widget. | ||
func rollingDisabled(st *scrollTracker, lines, height int) rollState { | ||
st.first = st.doScroll(lines, height) | ||
return rollingDisabled | ||
} | ||
|
||
// rollToEnd is a state in which the last line of the content is always | ||
// visible. When new content arrives, it is rolled upwards. | ||
func rollToEnd(st *scrollTracker, lines, height int) rollState { | ||
// If the user didn't scroll, just roll the content so that the last line | ||
// is visible. | ||
if st.scroll == 0 && st.scrollPage == 0 { | ||
st.first = normalizeScroll(math.MaxUint32, lines, height) | ||
return rollToEnd | ||
} | ||
|
||
st.first = st.doScroll(lines, height) | ||
if lastLineVisible(st.first, lines, height) { | ||
return rollToEnd | ||
} | ||
return rollingPaused | ||
} | ||
|
||
// rollingPaused is a state in which the user scrolled up and made the last | ||
// line scroll out of the view, so the content rolling is paused. | ||
func rollingPaused(st *scrollTracker, lines, height int) rollState { | ||
st.first = st.doScroll(lines, height) | ||
if lastLineVisible(st.first, lines, height) { | ||
return rollToEnd | ||
} | ||
return rollingPaused | ||
} | ||
|
||
// lastLineVisible returns true if the last line is visible given drawing that | ||
// starts from the first line, the number of lines and the height of the | ||
// canvas. | ||
func lastLineVisible(first, lines, height int) bool { | ||
return lines-first <= height | ||
} | ||
|
||
// normalizeScroll returns normalized position of the first line that should be | ||
// drawn when drawing the specified number of lines on a canvas with the | ||
// provided height. | ||
func normalizeScroll(first, lines, height int) int { | ||
if first < 0 || lines <= 0 || height <= 0 { | ||
return 0 | ||
} | ||
|
||
if lines <= height { | ||
return 0 // Scrolling not necessary if the content fits. | ||
} | ||
|
||
max := lines - height | ||
if first > max { | ||
return max | ||
} | ||
return first | ||
} |
Oops, something went wrong.