mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #720 from Textualize/opacity-changes
Changes to opacity
This commit is contained in:
@@ -4,18 +4,18 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
transition: color 300ms linear, background 300ms linear;
|
transition: color 300ms linear, background 300ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
*:hover {
|
*:hover {
|
||||||
/* tint: 30% red;
|
/* tint: 30% red;
|
||||||
/* outline: heavy red; */
|
/* outline: heavy red; */
|
||||||
}
|
}
|
||||||
|
|
||||||
App > Screen {
|
App > Screen {
|
||||||
|
|
||||||
background: $surface;
|
background: $surface;
|
||||||
color: $text-surface;
|
color: $text-surface;
|
||||||
layers: base sidebar;
|
layers: base sidebar;
|
||||||
|
|
||||||
color: $text-background;
|
color: $text-background;
|
||||||
@@ -23,12 +23,12 @@ App > Screen {
|
|||||||
layout: vertical;
|
layout: vertical;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#tree-container {
|
#tree-container {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 20;
|
height: 20;
|
||||||
margin: 1 3;
|
margin: 1 3;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
@@ -37,7 +37,7 @@ App > Screen {
|
|||||||
DirectoryTree {
|
DirectoryTree {
|
||||||
padding: 0 1;
|
padding: 0 1;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ DirectoryTree {
|
|||||||
DataTable {
|
DataTable {
|
||||||
/*border:heavy red;*/
|
/*border:heavy red;*/
|
||||||
/* tint: 10% green; */
|
/* tint: 10% green; */
|
||||||
/* opacity: 50%; */
|
/* text-opacity: 50%; */
|
||||||
padding: 1;
|
padding: 1;
|
||||||
margin: 1 2;
|
margin: 1 2;
|
||||||
height: 24;
|
height: 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
@@ -59,7 +59,7 @@ DataTable {
|
|||||||
width: 30;
|
width: 30;
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
offset-x: -100%;
|
offset-x: -100%;
|
||||||
|
|
||||||
transition: offset 500ms in_out_cubic;
|
transition: offset 500ms in_out_cubic;
|
||||||
layer: sidebar;
|
layer: sidebar;
|
||||||
}
|
}
|
||||||
@@ -97,8 +97,8 @@ DataTable {
|
|||||||
Tweet {
|
Tweet {
|
||||||
height:12;
|
height:12;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 2;
|
margin: 0 2;
|
||||||
|
|
||||||
background: $panel;
|
background: $panel;
|
||||||
color: $text-panel;
|
color: $text-panel;
|
||||||
layout: vertical;
|
layout: vertical;
|
||||||
@@ -121,9 +121,9 @@ Tweet {
|
|||||||
layout: vertical;
|
layout: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,12 +133,12 @@ TweetHeader {
|
|||||||
color: $text-accent
|
color: $text-accent
|
||||||
}
|
}
|
||||||
|
|
||||||
TweetBody {
|
TweetBody {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
color: $text-panel;
|
color: $text-panel;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 0 1 0 0;
|
padding: 0 1 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tweet.scroll-horizontal TweetBody {
|
Tweet.scroll-horizontal TweetBody {
|
||||||
@@ -158,7 +158,7 @@ Tweet.scroll-horizontal TweetBody {
|
|||||||
/* padding: 1 0 0 0 ; */
|
/* padding: 1 0 0 0 ; */
|
||||||
|
|
||||||
transition: background 400ms in_out_cubic, color 400ms in_out_cubic;
|
transition: background 400ms in_out_cubic, color 400ms in_out_cubic;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
@@ -178,7 +178,7 @@ Tweet.scroll-horizontal TweetBody {
|
|||||||
color: $text-accent;
|
color: $text-accent;
|
||||||
background: $accent;
|
background: $accent;
|
||||||
height: 1;
|
height: 1;
|
||||||
|
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
dock:bottom;
|
dock:bottom;
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ Error {
|
|||||||
color: $text-error;
|
color: $text-error;
|
||||||
border-top: tall $error-darken-2;
|
border-top: tall $error-darken-2;
|
||||||
border-bottom: tall $error-darken-2;
|
border-bottom: tall $error-darken-2;
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
align-horizontal: center;
|
align-horizontal: center;
|
||||||
@@ -226,21 +226,21 @@ Warning {
|
|||||||
color: $text-warning-fade-1;
|
color: $text-warning-fade-1;
|
||||||
border-top: tall $warning-darken-2;
|
border-top: tall $warning-darken-2;
|
||||||
border-bottom: tall $warning-darken-2;
|
border-bottom: tall $warning-darken-2;
|
||||||
|
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
align-horizontal: center;
|
align-horizontal: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
Success {
|
Success {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
height:auto;
|
height:auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: $success;
|
background: $success;
|
||||||
color: $text-success-fade-1;
|
color: $text-success-fade-1;
|
||||||
|
|
||||||
border-top: hkey $success-darken-2;
|
border-top: hkey $success-darken-2;
|
||||||
border-bottom: hkey $success-darken-2;
|
border-bottom: hkey $success-darken-2;
|
||||||
|
|
||||||
text-style: bold ;
|
text-style: bold ;
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ Stopwatch {
|
|||||||
|
|
||||||
TimeDisplay {
|
TimeDisplay {
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
opacity: 60%;
|
text-opacity: 60%;
|
||||||
height: 3;
|
height: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
width: 16;
|
width: 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
#start {
|
#start {
|
||||||
@@ -30,14 +30,14 @@ Button {
|
|||||||
dock: right;
|
dock: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started {
|
.started {
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
background: $success;
|
background: $success;
|
||||||
color: $text-success;
|
color: $text-success;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started TimeDisplay {
|
.started TimeDisplay {
|
||||||
opacity: 100%;
|
text-opacity: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started #start {
|
.started #start {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ Stopwatch {
|
|||||||
|
|
||||||
TimeDisplay {
|
TimeDisplay {
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
opacity: 60%;
|
text-opacity: 60%;
|
||||||
height: 3;
|
height: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
width: 16;
|
width: 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
#start {
|
#start {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ Stopwatch {
|
|||||||
|
|
||||||
TimeDisplay {
|
TimeDisplay {
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
opacity: 60%;
|
text-opacity: 60%;
|
||||||
height: 3;
|
height: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
width: 16;
|
width: 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
#start {
|
#start {
|
||||||
@@ -30,14 +30,14 @@ Button {
|
|||||||
dock: right;
|
dock: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started {
|
.started {
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
background: $success;
|
background: $success;
|
||||||
color: $text-success;
|
color: $text-success;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started TimeDisplay {
|
.started TimeDisplay {
|
||||||
opacity: 100%;
|
text-opacity: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.started #start {
|
.started #start {
|
||||||
|
|||||||
31
docs/examples/styles/opacity.css
Normal file
31
docs/examples/styles/opacity.css
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#zero-opacity {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quarter-opacity {
|
||||||
|
opacity: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#half-opacity {
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#three-quarter-opacity {
|
||||||
|
opacity: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#full-opacity {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
Screen {
|
||||||
|
background: antiquewhite;
|
||||||
|
}
|
||||||
|
|
||||||
|
Static {
|
||||||
|
height: 1fr;
|
||||||
|
border: outer dodgerblue;
|
||||||
|
background: lightseagreen;
|
||||||
|
content-align: center middle;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
14
docs/examples/styles/opacity.py
Normal file
14
docs/examples/styles/opacity.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from textual.app import App
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class OpacityApp(App):
|
||||||
|
def compose(self):
|
||||||
|
yield Static("opacity: 0%", id="zero-opacity")
|
||||||
|
yield Static("opacity: 25%", id="quarter-opacity")
|
||||||
|
yield Static("opacity: 50%", id="half-opacity")
|
||||||
|
yield Static("opacity: 75%", id="three-quarter-opacity")
|
||||||
|
yield Static("opacity: 100%", id="full-opacity")
|
||||||
|
|
||||||
|
|
||||||
|
app = OpacityApp(css_path="opacity.css")
|
||||||
25
docs/examples/styles/text_opacity.css
Normal file
25
docs/examples/styles/text_opacity.css
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#zero-opacity {
|
||||||
|
text-opacity: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#quarter-opacity {
|
||||||
|
text-opacity: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#half-opacity {
|
||||||
|
text-opacity: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#three-quarter-opacity {
|
||||||
|
text-opacity: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#full-opacity {
|
||||||
|
text-opacity: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
Static {
|
||||||
|
height: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
14
docs/examples/styles/text_opacity.py
Normal file
14
docs/examples/styles/text_opacity.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from textual.app import App
|
||||||
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
|
class TextOpacityApp(App):
|
||||||
|
def compose(self):
|
||||||
|
yield Static("text-opacity: 0%", id="zero-opacity")
|
||||||
|
yield Static("text-opacity: 25%", id="quarter-opacity")
|
||||||
|
yield Static("text-opacity: 50%", id="half-opacity")
|
||||||
|
yield Static("text-opacity: 75%", id="three-quarter-opacity")
|
||||||
|
yield Static("text-opacity: 100%", id="full-opacity")
|
||||||
|
|
||||||
|
|
||||||
|
app = TextOpacityApp(css_path="text_opacity.css")
|
||||||
54
docs/styles/opacity.md
Normal file
54
docs/styles/opacity.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Opacity
|
||||||
|
|
||||||
|
The `opacity` property can be used to make a widget partially or fully transparent.
|
||||||
|
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
opacity: <FRACTIONAL>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Values
|
||||||
|
|
||||||
|
As a fractional property, `opacity` can be set to either a float (between 0 and 1),
|
||||||
|
or a percentage, e.g. `45%`.
|
||||||
|
Float values will be clamped between 0 and 1.
|
||||||
|
Percentage values will be clamped between 0% and 100%.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This example shows, from top to bottom, increasing opacity values.
|
||||||
|
|
||||||
|
=== "opacity.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/styles/opacity.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "opacity.css"
|
||||||
|
|
||||||
|
```scss
|
||||||
|
--8<-- "docs/examples/styles/opacity.css"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Output"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/styles/opacity.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
```sass
|
||||||
|
/* Fade the widget to 50% against its parent's background */
|
||||||
|
Widget {
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Fade the widget to 50% against its parent's background
|
||||||
|
widget.styles.opacity = "50%"
|
||||||
|
```
|
||||||
53
docs/styles/text_opacity.md
Normal file
53
docs/styles/text_opacity.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Text-opacity
|
||||||
|
|
||||||
|
The `text-opacity` blends the color of the content of a widget with the color of the background.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
text-opacity: <FRACTIONAL>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Values
|
||||||
|
|
||||||
|
As a fractional property, `text-opacity` can be set to either a float (between 0 and 1),
|
||||||
|
or a percentage, e.g. `45%`.
|
||||||
|
Float values will be clamped between 0 and 1.
|
||||||
|
Percentage values will be clamped between 0% and 100%.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This example shows, from top to bottom, increasing text-opacity values.
|
||||||
|
|
||||||
|
=== "text_opacity.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/styles/text_opacity.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "text_opacity.css"
|
||||||
|
|
||||||
|
```css
|
||||||
|
--8<-- "docs/examples/styles/text_opacity.css"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Output"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/styles/text_opacity.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
```sass
|
||||||
|
/* Set the text to be "half-faded" against the background of the widget */
|
||||||
|
Widget {
|
||||||
|
text-opacity: 50%;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Set the text to be "half-faded" against the background of the widget
|
||||||
|
widget.styles.text_opacity = "50%"
|
||||||
|
```
|
||||||
@@ -4,33 +4,33 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
transition: color 300ms linear, background 300ms linear;
|
transition: color 300ms linear, background 300ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
*:hover {
|
*:hover {
|
||||||
/* tint: 30% red;
|
/* tint: 30% red;
|
||||||
/* outline: heavy red; */
|
/* outline: heavy red; */
|
||||||
}
|
}
|
||||||
|
|
||||||
App > Screen {
|
App > Screen {
|
||||||
|
|
||||||
background: $surface;
|
background: $surface;
|
||||||
color: $text-surface;
|
color: $text-surface;
|
||||||
layers: sidebar;
|
layers: sidebar;
|
||||||
|
|
||||||
color: $text-background;
|
color: $text-background;
|
||||||
background: $background;
|
background: $background;
|
||||||
layout: vertical;
|
layout: vertical;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTable {
|
DataTable {
|
||||||
/*border:heavy red;*/
|
/*border:heavy red;*/
|
||||||
/* tint: 10% green; */
|
/* tint: 10% green; */
|
||||||
/* opacity: 50%; */
|
/* text-opacity: 50%; */
|
||||||
padding: 1;
|
padding: 1;
|
||||||
margin: 1 2;
|
margin: 1 2;
|
||||||
height: 12;
|
height: 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
@@ -39,7 +39,7 @@ DataTable {
|
|||||||
dock: left;
|
dock: left;
|
||||||
width: 30;
|
width: 30;
|
||||||
offset-x: -100%;
|
offset-x: -100%;
|
||||||
|
|
||||||
transition: offset 500ms in_out_cubic;
|
transition: offset 500ms in_out_cubic;
|
||||||
layer: sidebar;
|
layer: sidebar;
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ DataTable {
|
|||||||
background: $secondary-background;
|
background: $secondary-background;
|
||||||
height: 1;
|
height: 1;
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
|
|
||||||
dock: top;
|
dock: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ DataTable {
|
|||||||
Tweet {
|
Tweet {
|
||||||
height:12;
|
height:12;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|
||||||
background: $panel;
|
background: $panel;
|
||||||
color: $text-panel;
|
color: $text-panel;
|
||||||
layout: vertical;
|
layout: vertical;
|
||||||
@@ -100,7 +100,7 @@ Tweet {
|
|||||||
|
|
||||||
|
|
||||||
.scrollable {
|
.scrollable {
|
||||||
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
margin: 1 2;
|
margin: 1 2;
|
||||||
height: 20;
|
height: 20;
|
||||||
@@ -108,9 +108,9 @@ Tweet {
|
|||||||
layout: vertical;
|
layout: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -120,12 +120,12 @@ TweetHeader {
|
|||||||
color: $text-accent
|
color: $text-accent
|
||||||
}
|
}
|
||||||
|
|
||||||
TweetBody {
|
TweetBody {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $panel;
|
background: $panel;
|
||||||
color: $text-panel;
|
color: $text-panel;
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 0 1 0 0;
|
padding: 0 1 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tweet.scroll-horizontal TweetBody {
|
Tweet.scroll-horizontal TweetBody {
|
||||||
@@ -145,7 +145,7 @@ Tweet.scroll-horizontal TweetBody {
|
|||||||
/* padding: 1 0 0 0 ; */
|
/* padding: 1 0 0 0 ; */
|
||||||
|
|
||||||
transition: background 400ms in_out_cubic, color 400ms in_out_cubic;
|
transition: background 400ms in_out_cubic, color 400ms in_out_cubic;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
@@ -165,7 +165,7 @@ Tweet.scroll-horizontal TweetBody {
|
|||||||
color: $text-accent;
|
color: $text-accent;
|
||||||
background: $accent;
|
background: $accent;
|
||||||
height: 1;
|
height: 1;
|
||||||
|
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
dock:bottom;
|
dock:bottom;
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,7 @@ Error {
|
|||||||
color: $text-error;
|
color: $text-error;
|
||||||
border-top: tall $error-darken-2;
|
border-top: tall $error-darken-2;
|
||||||
border-bottom: tall $error-darken-2;
|
border-bottom: tall $error-darken-2;
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
align-horizontal: center;
|
align-horizontal: center;
|
||||||
@@ -213,21 +213,21 @@ Warning {
|
|||||||
color: $text-warning-fade-1;
|
color: $text-warning-fade-1;
|
||||||
border-top: tall $warning-darken-2;
|
border-top: tall $warning-darken-2;
|
||||||
border-bottom: tall $warning-darken-2;
|
border-bottom: tall $warning-darken-2;
|
||||||
|
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
align-horizontal: center;
|
align-horizontal: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
Success {
|
Success {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
height:auto;
|
height:auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: $success;
|
background: $success;
|
||||||
color: $text-success-fade-1;
|
color: $text-success-fade-1;
|
||||||
|
|
||||||
border-top: hkey $success-darken-2;
|
border-top: hkey $success-darken-2;
|
||||||
border-bottom: hkey $success-darken-2;
|
border-bottom: hkey $success-darken-2;
|
||||||
|
|
||||||
text-style: bold ;
|
text-style: bold ;
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ nav:
|
|||||||
- "styles/min_height.md"
|
- "styles/min_height.md"
|
||||||
- "styles/min_width.md"
|
- "styles/min_width.md"
|
||||||
- "styles/offset.md"
|
- "styles/offset.md"
|
||||||
|
- "styles/opacity.md"
|
||||||
- "styles/outline.md"
|
- "styles/outline.md"
|
||||||
- "styles/overflow.md"
|
- "styles/overflow.md"
|
||||||
- "styles/padding.md"
|
- "styles/padding.md"
|
||||||
@@ -59,6 +60,7 @@ nav:
|
|||||||
- "styles/scrollbar_size.md"
|
- "styles/scrollbar_size.md"
|
||||||
- "styles/text_align.md"
|
- "styles/text_align.md"
|
||||||
- "styles/text_style.md"
|
- "styles/text_style.md"
|
||||||
|
- "styles/text_opacity.md"
|
||||||
- "styles/tint.md"
|
- "styles/tint.md"
|
||||||
- "styles/visibility.md"
|
- "styles/visibility.md"
|
||||||
- "styles/width.md"
|
- "styles/width.md"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
Button {
|
Button {
|
||||||
padding-left: 1;
|
padding-left: 1;
|
||||||
padding-right: 1;
|
padding-right: 1;
|
||||||
|
margin: 3;
|
||||||
|
text-opacity: 30%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,8 @@
|
|||||||
Screen {
|
Screen {
|
||||||
background: lightcoral;
|
background: darkslategrey;
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar {
|
|
||||||
color: $text-panel;
|
|
||||||
background: $panel;
|
|
||||||
dock: left;
|
|
||||||
width: 30;
|
|
||||||
offset-x: -100%;
|
|
||||||
transition: offset 500ms in_out_cubic 2s;
|
|
||||||
layer: sidebar;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar.-active {
|
|
||||||
offset-x: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.box1 {
|
.box1 {
|
||||||
background: orangered;
|
background: darkmagenta;
|
||||||
height: 12;
|
width: auto;
|
||||||
width: 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box2 {
|
|
||||||
background: blueviolet;
|
|
||||||
height: 6;
|
|
||||||
width: 12;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from rich.console import RenderableType
|
|
||||||
|
|
||||||
from textual import events
|
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.widget import Widget
|
from textual.widgets import Static
|
||||||
|
|
||||||
|
|
||||||
class Box(Widget, can_focus=True):
|
|
||||||
CSS = "#box {background: blue;}"
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, id: str | None = None, classes: str | None = None, *children: Widget
|
|
||||||
):
|
|
||||||
super().__init__(*children, id=id, classes=classes)
|
|
||||||
|
|
||||||
def render(self) -> RenderableType:
|
|
||||||
return "Box"
|
|
||||||
|
|
||||||
|
|
||||||
class JustABox(App):
|
class JustABox(App):
|
||||||
def on_load(self):
|
|
||||||
self.bind("s", "toggle_class('#sidebar', '-active')", description="Sidebar")
|
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
self.box = Box(classes="box1")
|
yield Static("Hello, world!", classes="box1")
|
||||||
yield self.box
|
|
||||||
yield Box(classes="box2")
|
|
||||||
yield Widget(id="sidebar")
|
|
||||||
|
|
||||||
def key_a(self):
|
|
||||||
self.animator.animate(
|
|
||||||
self.box.styles,
|
|
||||||
"opacity",
|
|
||||||
value=0.0,
|
|
||||||
duration=2.0,
|
|
||||||
delay=2.0,
|
|
||||||
on_complete=self.box.remove,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def on_key(self, event: events.Key) -> None:
|
|
||||||
await self.dispatch_key(event)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ Widget:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
opacity: 1;
|
text-opacity: 1;
|
||||||
color: $text;
|
color: $text;
|
||||||
background: $background;
|
background: $background;
|
||||||
height: 3;
|
height: 3;
|
||||||
@@ -62,5 +62,5 @@ Widget:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#footer.dim {
|
#footer.dim {
|
||||||
opacity: 0.5;
|
text-opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ DirectoryTree {
|
|||||||
DataTable {
|
DataTable {
|
||||||
/*border:heavy red;*/
|
/*border:heavy red;*/
|
||||||
/* tint: 10% green; */
|
/* tint: 10% green; */
|
||||||
/* opacity: 50%; */
|
/* text-opacity: 50%; */
|
||||||
padding: 1;
|
padding: 1;
|
||||||
margin: 1 2;
|
margin: 1 2;
|
||||||
height: 24;
|
height: 24;
|
||||||
|
|||||||
45
src/textual/_opacity.py
Normal file
45
src/textual/_opacity.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from rich.segment import Segment
|
||||||
|
from rich.style import Style
|
||||||
|
|
||||||
|
from textual.color import Color
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_opacity(
|
||||||
|
segments: Iterable[Segment],
|
||||||
|
base_background: Color,
|
||||||
|
opacity: float,
|
||||||
|
) -> Iterable[Segment]:
|
||||||
|
"""Takes an iterable of foreground Segments and blends them into the supplied
|
||||||
|
background color, yielding copies of the Segments with blended foreground and
|
||||||
|
background colors applied.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
segments (Iterable[Segment]): The segments in the foreground.
|
||||||
|
base_background (Color): The background color to blend foreground into.
|
||||||
|
opacity (float): The blending factor. A value of 1.0 means output segments will
|
||||||
|
have identical foreground and background colors to input segments.
|
||||||
|
"""
|
||||||
|
_Segment = Segment
|
||||||
|
from_rich_color = Color.from_rich_color
|
||||||
|
from_color = Style.from_color
|
||||||
|
blend = base_background.blend
|
||||||
|
for segment in segments:
|
||||||
|
text, style, _ = segment
|
||||||
|
if not style:
|
||||||
|
yield segment
|
||||||
|
continue
|
||||||
|
|
||||||
|
blended_style = style
|
||||||
|
if style.color:
|
||||||
|
color = from_rich_color(style.color)
|
||||||
|
blended_foreground = blend(color, factor=opacity)
|
||||||
|
blended_style += from_color(color=blended_foreground.rich_color)
|
||||||
|
|
||||||
|
if style.bgcolor:
|
||||||
|
bgcolor = from_rich_color(style.bgcolor)
|
||||||
|
blended_background = blend(bgcolor, factor=opacity)
|
||||||
|
blended_style += from_color(bgcolor=blended_background.rich_color)
|
||||||
|
|
||||||
|
yield _Segment(text, blended_style)
|
||||||
@@ -7,11 +7,12 @@ from rich.segment import Segment
|
|||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
|
|
||||||
from ._border import get_box, render_row
|
from ._border import get_box, render_row
|
||||||
|
from ._opacity import _apply_opacity
|
||||||
from ._segment_tools import line_crop, line_pad, line_trim
|
from ._segment_tools import line_crop, line_pad, line_trim
|
||||||
from ._types import Lines
|
from ._types import Lines
|
||||||
from .color import Color
|
from .color import Color
|
||||||
from .geometry import Region, Size, Spacing
|
from .geometry import Region, Size, Spacing
|
||||||
from .renderables.opacity import Opacity
|
from .renderables.text_opacity import TextOpacity
|
||||||
from .renderables.tint import Tint
|
from .renderables.tint import Tint
|
||||||
|
|
||||||
if sys.version_info >= (3, 10):
|
if sys.version_info >= (3, 10):
|
||||||
@@ -237,10 +238,13 @@ class StylesCache:
|
|||||||
Returns:
|
Returns:
|
||||||
list[Segment]: New list of segments
|
list[Segment]: New list of segments
|
||||||
"""
|
"""
|
||||||
if styles.opacity != 1.0:
|
if styles.text_opacity != 1.0:
|
||||||
segments = Opacity.process_segments(segments, styles.opacity)
|
segments = TextOpacity.process_segments(segments, styles.text_opacity)
|
||||||
if styles.tint.a:
|
if styles.tint.a:
|
||||||
segments = Tint.process_segments(segments, styles.tint)
|
segments = Tint.process_segments(segments, styles.tint)
|
||||||
|
if styles.opacity != 1.0:
|
||||||
|
segments = _apply_opacity(segments, base_background, styles.opacity)
|
||||||
|
segments = list(segments)
|
||||||
return segments if isinstance(segments, list) else list(segments)
|
return segments if isinstance(segments, list) else list(segments)
|
||||||
|
|
||||||
line: Iterable[Segment]
|
line: Iterable[Segment]
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ class StylesBuilder:
|
|||||||
"visibility", valid_values=list(VALID_VISIBILITY), context="css"
|
"visibility", valid_values=list(VALID_VISIBILITY), context="css"
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_opacity(self, name: str, tokens: list[Token]) -> None:
|
def _process_fractional(self, name: str, tokens: list[Token]) -> None:
|
||||||
if not tokens:
|
if not tokens:
|
||||||
return
|
return
|
||||||
token = tokens[0]
|
token = tokens[0]
|
||||||
@@ -342,16 +342,17 @@ class StylesBuilder:
|
|||||||
else:
|
else:
|
||||||
token_name = token.name
|
token_name = token.name
|
||||||
value = token.value
|
value = token.value
|
||||||
|
rule_name = name.replace("-", "_")
|
||||||
if token_name == "scalar" and value.endswith("%"):
|
if token_name == "scalar" and value.endswith("%"):
|
||||||
try:
|
try:
|
||||||
opacity = percentage_string_to_float(value)
|
text_opacity = percentage_string_to_float(value)
|
||||||
self.styles.set_rule(name, opacity)
|
self.styles.set_rule(rule_name, text_opacity)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
error = True
|
error = True
|
||||||
elif token_name == "number":
|
elif token_name == "number":
|
||||||
try:
|
try:
|
||||||
opacity = clamp(float(value), 0, 1)
|
text_opacity = clamp(float(value), 0, 1)
|
||||||
self.styles.set_rule(name, opacity)
|
self.styles.set_rule(rule_name, text_opacity)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
error = True
|
error = True
|
||||||
else:
|
else:
|
||||||
@@ -360,6 +361,9 @@ class StylesBuilder:
|
|||||||
if error:
|
if error:
|
||||||
self.error(name, token, fractional_property_help_text(name, context="css"))
|
self.error(name, token, fractional_property_help_text(name, context="css"))
|
||||||
|
|
||||||
|
process_opacity = _process_fractional
|
||||||
|
process_text_opacity = _process_fractional
|
||||||
|
|
||||||
def _process_space(self, name: str, tokens: list[Token]) -> None:
|
def _process_space(self, name: str, tokens: list[Token]) -> None:
|
||||||
space: list[int] = []
|
space: list[int] = []
|
||||||
append = space.append
|
append = space.append
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class RulesMap(TypedDict, total=False):
|
|||||||
text_style: Style
|
text_style: Style
|
||||||
|
|
||||||
opacity: float
|
opacity: float
|
||||||
|
text_opacity: float
|
||||||
|
|
||||||
padding: Spacing
|
padding: Spacing
|
||||||
margin: Spacing
|
margin: Spacing
|
||||||
@@ -185,6 +186,7 @@ class StylesBase(ABC):
|
|||||||
"color",
|
"color",
|
||||||
"background",
|
"background",
|
||||||
"opacity",
|
"opacity",
|
||||||
|
"text_opacity",
|
||||||
"tint",
|
"tint",
|
||||||
"scrollbar_color",
|
"scrollbar_color",
|
||||||
"scrollbar_color_hover",
|
"scrollbar_color_hover",
|
||||||
@@ -205,6 +207,7 @@ class StylesBase(ABC):
|
|||||||
text_style = StyleFlagsProperty()
|
text_style = StyleFlagsProperty()
|
||||||
|
|
||||||
opacity = FractionalProperty()
|
opacity = FractionalProperty()
|
||||||
|
text_opacity = FractionalProperty()
|
||||||
|
|
||||||
padding = SpacingProperty()
|
padding = SpacingProperty()
|
||||||
margin = SpacingProperty()
|
margin = SpacingProperty()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ def _get_blended_style_cached(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Opacity:
|
class TextOpacity:
|
||||||
"""Blend foreground in to background."""
|
"""Blend foreground in to background."""
|
||||||
|
|
||||||
def __init__(self, renderable: RenderableType, opacity: float = 1.0) -> None:
|
def __init__(self, renderable: RenderableType, opacity: float = 1.0) -> None:
|
||||||
@@ -96,7 +96,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
console.print(panel)
|
console.print(panel)
|
||||||
|
|
||||||
opacity_panel = Opacity(panel, opacity=0.5)
|
opacity_panel = TextOpacity(panel, opacity=0.5)
|
||||||
console.print(opacity_panel)
|
console.print(opacity_panel)
|
||||||
|
|
||||||
def frange(start, end, step):
|
def frange(start, end, step):
|
||||||
@@ -29,14 +29,14 @@ class HeaderClock(Widget):
|
|||||||
"""Display a clock on the right of the header."""
|
"""Display a clock on the right of the header."""
|
||||||
|
|
||||||
CSS = """
|
CSS = """
|
||||||
HeaderClock {
|
HeaderClock {
|
||||||
dock: right;
|
dock: right;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0 1;
|
padding: 0 1;
|
||||||
background: $secondary-background-lighten-1;
|
background: $secondary-background-lighten-1;
|
||||||
color: $text-secondary-background;
|
color: $text-secondary-background;
|
||||||
opacity: 85%;
|
text-opacity: 85%;
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -51,9 +51,9 @@ class HeaderTitle(Widget):
|
|||||||
"""Display the title / subtitle in the header."""
|
"""Display the title / subtitle in the header."""
|
||||||
|
|
||||||
CSS = """
|
CSS = """
|
||||||
HeaderTitle {
|
HeaderTitle {
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class Header(Widget):
|
|||||||
height: 1;
|
height: 1;
|
||||||
}
|
}
|
||||||
Header.tall {
|
Header.tall {
|
||||||
height: 3;
|
height: 3;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from textual import events
|
|||||||
from textual._layout_resolve import layout_resolve, Edge
|
from textual._layout_resolve import layout_resolve, Edge
|
||||||
from textual.keys import Keys
|
from textual.keys import Keys
|
||||||
from textual.reactive import Reactive
|
from textual.reactive import Reactive
|
||||||
from textual.renderables.opacity import Opacity
|
from textual.renderables.text_opacity import TextOpacity
|
||||||
from textual.renderables.underline_bar import UnderlineBar
|
from textual.renderables.underline_bar import UnderlineBar
|
||||||
from textual.widget import Widget
|
from textual.widget import Widget
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ class TabsRenderable:
|
|||||||
style=inactive_tab_style
|
style=inactive_tab_style
|
||||||
+ Style.from_meta({"@click": f"range_clicked('{tab.name}')"}),
|
+ Style.from_meta({"@click": f"range_clicked('{tab.name}')"}),
|
||||||
)
|
)
|
||||||
dimmed_tab_content = Opacity(
|
dimmed_tab_content = TextOpacity(
|
||||||
tab_content, opacity=self.inactive_text_opacity
|
tab_content, opacity=self.inactive_text_opacity
|
||||||
)
|
)
|
||||||
segments = console.render(dimmed_tab_content)
|
segments = console.render(dimmed_tab_content)
|
||||||
|
|||||||
@@ -1099,15 +1099,15 @@ class TestParseOpacity:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_opacity_to_styles(self, css_value, styles_value):
|
def test_opacity_to_styles(self, css_value, styles_value):
|
||||||
css = f"#some-widget {{ opacity: {css_value} }}"
|
css = f"#some-widget {{ text-opacity: {css_value} }}"
|
||||||
stylesheet = Stylesheet()
|
stylesheet = Stylesheet()
|
||||||
stylesheet.add_source(css)
|
stylesheet.add_source(css)
|
||||||
|
|
||||||
assert stylesheet.rules[0].styles.opacity == styles_value
|
assert stylesheet.rules[0].styles.text_opacity == styles_value
|
||||||
assert not stylesheet.rules[0].errors
|
assert not stylesheet.rules[0].errors
|
||||||
|
|
||||||
def test_opacity_invalid_value(self):
|
def test_opacity_invalid_value(self):
|
||||||
css = "#some-widget { opacity: 123x }"
|
css = "#some-widget { text-opacity: 123x }"
|
||||||
stylesheet = Stylesheet()
|
stylesheet = Stylesheet()
|
||||||
|
|
||||||
with pytest.raises(StylesheetParseError):
|
with pytest.raises(StylesheetParseError):
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ def test_render_styles_border():
|
|||||||
|
|
||||||
def test_get_opacity_default():
|
def test_get_opacity_default():
|
||||||
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
||||||
assert styles.opacity == 1.0
|
assert styles.text_opacity == 1.0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -136,14 +136,14 @@ def test_get_opacity_default():
|
|||||||
)
|
)
|
||||||
def test_opacity_set_then_get(set_value, expected):
|
def test_opacity_set_then_get(set_value, expected):
|
||||||
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
||||||
styles.opacity = set_value
|
styles.text_opacity = set_value
|
||||||
assert styles.opacity == expected
|
assert styles.text_opacity == expected
|
||||||
|
|
||||||
|
|
||||||
def test_opacity_set_invalid_type_error():
|
def test_opacity_set_invalid_type_error():
|
||||||
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
styles = RenderStyles(DOMNode(), Styles(), Styles())
|
||||||
with pytest.raises(StyleValueError):
|
with pytest.raises(StyleValueError):
|
||||||
styles.opacity = "invalid value"
|
styles.text_opacity = "invalid value"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import pytest
|
|||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from tests.utilities.render import render
|
from tests.utilities.render import render
|
||||||
from textual.renderables.opacity import Opacity
|
from textual.renderables.text_opacity import TextOpacity
|
||||||
|
|
||||||
STOP = "\x1b[0m"
|
STOP = "\x1b[0m"
|
||||||
|
|
||||||
@@ -12,39 +12,39 @@ def text():
|
|||||||
return Text("Hello, world!", style="#ff0000 on #00ff00", end="")
|
return Text("Hello, world!", style="#ff0000 on #00ff00", end="")
|
||||||
|
|
||||||
|
|
||||||
def test_simple_opacity(text):
|
def test_simple_text_opacity(text):
|
||||||
blended_red_on_green = "\x1b[38;2;127;127;0;48;2;0;255;0m"
|
blended_red_on_green = "\x1b[38;2;127;127;0;48;2;0;255;0m"
|
||||||
assert render(Opacity(text, opacity=.5)) == (
|
assert render(TextOpacity(text, opacity=.5)) == (
|
||||||
f"{blended_red_on_green}Hello, world!{STOP}"
|
f"{blended_red_on_green}Hello, world!{STOP}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_value_zero_sets_foreground_color_to_background_color(text):
|
def test_value_zero_sets_foreground_color_to_background_color(text):
|
||||||
foreground = background = "0;255;0"
|
foreground = background = "0;255;0"
|
||||||
assert render(Opacity(text, opacity=0)) == (
|
assert render(TextOpacity(text, opacity=0)) == (
|
||||||
f"\x1b[38;2;{foreground};48;2;{background}mHello, world!{STOP}"
|
f"\x1b[38;2;{foreground};48;2;{background}mHello, world!{STOP}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_opacity_value_of_one_noop(text):
|
def test_text_opacity_value_of_one_noop(text):
|
||||||
assert render(Opacity(text, opacity=1)) == render(text)
|
assert render(TextOpacity(text, opacity=1)) == render(text)
|
||||||
|
|
||||||
|
|
||||||
def test_ansi_colors_noop():
|
def test_ansi_colors_noop():
|
||||||
ansi_colored_text = Text("Hello, world!", style="red on green", end="")
|
ansi_colored_text = Text("Hello, world!", style="red on green", end="")
|
||||||
assert render(Opacity(ansi_colored_text, opacity=.5)) == render(ansi_colored_text)
|
assert render(TextOpacity(ansi_colored_text, opacity=.5)) == render(ansi_colored_text)
|
||||||
|
|
||||||
|
|
||||||
def test_opacity_no_style_noop():
|
def test_text_opacity_no_style_noop():
|
||||||
text_no_style = Text("Hello, world!", end="")
|
text_no_style = Text("Hello, world!", end="")
|
||||||
assert render(Opacity(text_no_style, opacity=.2)) == render(text_no_style)
|
assert render(TextOpacity(text_no_style, opacity=.2)) == render(text_no_style)
|
||||||
|
|
||||||
|
|
||||||
def test_opacity_only_fg_noop():
|
def test_text_opacity_only_fg_noop():
|
||||||
text_only_fg = Text("Hello, world!", style="#ff0000", end="")
|
text_only_fg = Text("Hello, world!", style="#ff0000", end="")
|
||||||
assert render(Opacity(text_only_fg, opacity=.5)) == render(text_only_fg)
|
assert render(TextOpacity(text_only_fg, opacity=.5)) == render(text_only_fg)
|
||||||
|
|
||||||
|
|
||||||
def test_opacity_only_bg_noop():
|
def test_text_opacity_only_bg_noop():
|
||||||
text_only_bg = Text("Hello, world!", style="on #ff0000", end="")
|
text_only_bg = Text("Hello, world!", style="on #ff0000", end="")
|
||||||
assert render(Opacity(text_only_bg, opacity=.5)) == render(text_only_bg)
|
assert render(TextOpacity(text_only_bg, opacity=.5)) == render(text_only_bg)
|
||||||
Reference in New Issue
Block a user