Skip to content

Commit

Permalink
Merge a386289 into 2f210f3
Browse files Browse the repository at this point in the history
  • Loading branch information
mum4k committed May 14, 2018
2 parents 2f210f3 + a386289 commit b256e34
Show file tree
Hide file tree
Showing 2 changed files with 479 additions and 0 deletions.
147 changes: 147 additions & 0 deletions widgets/text/scroll.go
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
}

0 comments on commit b256e34

Please sign in to comment.