2018-11-29 02:42:05 +11:00
//! This module contains the logic to style an object that contains some 'content' which can be styled.
2018-07-30 05:30:09 +10:00
2019-01-28 07:16:14 +11:00
use super ::{ color , Color , ObjectStyle } ;
//use Screen;
use crossterm_utils ::{ Result , TerminalOutput } ;
2018-11-15 02:53:27 +11:00
use std ::fmt ::{ self , Display , Formatter } ;
use std ::io ::Write ;
2019-01-02 08:27:50 +11:00
use std ::result ;
2019-01-28 07:16:14 +11:00
use std ::sync ::Arc ;
2018-01-28 04:48:49 +11:00
2018-07-29 03:46:05 +10:00
use super ::Attribute ;
2018-01-07 07:31:14 +11:00
2018-03-11 03:33:06 +11:00
/// Struct that contains both the style and the content wits can be styled.
2018-08-12 01:58:15 +10:00
pub struct StyledObject < D : Display > {
2018-01-04 00:43:54 +11:00
pub object_style : ObjectStyle ,
2018-01-07 07:31:14 +11:00
pub content : D ,
2018-01-04 00:43:54 +11:00
}
2018-09-22 05:36:25 +10:00
impl < ' a , D : Display + ' a > StyledObject < D > {
2018-11-15 02:53:27 +11:00
/// Set the foreground of the styled object to the passed `Color`.
2018-01-07 07:31:14 +11:00
///
2019-01-28 07:16:14 +11:00
/// # Remarks
2018-08-14 05:04:07 +10:00
///
2019-01-28 07:16:14 +11:00
/// This methods consumes 'self', and works like a builder.
/// By having this functionality you can do: `with().on().attr()`
2018-08-12 01:58:15 +10:00
pub fn with ( mut self , foreground_color : Color ) -> StyledObject < D > {
2018-01-04 00:43:54 +11:00
self . object_style = self . object_style . fg ( foreground_color ) ;
self
}
2018-11-15 02:53:27 +11:00
/// Set the background of the styled object to the passed `Color`.
2018-01-07 07:31:14 +11:00
///
2019-01-28 07:16:14 +11:00
/// # Remarks
2018-08-14 05:04:07 +10:00
///
2019-01-28 07:16:14 +11:00
/// This methods consumes 'self', and works like a builder.
/// By having this functionality you can do: `with().on().attr()`
2018-08-12 01:58:15 +10:00
pub fn on ( mut self , background_color : Color ) -> StyledObject < D > {
2018-01-04 00:43:54 +11:00
self . object_style = self . object_style . bg ( background_color ) ;
self
}
2018-01-26 04:26:08 +11:00
2018-11-15 02:53:27 +11:00
/// Set the attribute of an styled object to the passed `Attribute`.
2018-01-28 04:26:08 +11:00
///
2019-01-28 07:16:14 +11:00
/// # Remarks
2018-01-28 04:26:08 +11:00
///
2019-01-28 07:16:14 +11:00
/// This methods consumes 'self', and works like a builder.
/// By having this functionality you can do: `with().on().attr()`
2018-08-12 01:58:15 +10:00
pub fn attr ( mut self , attr : Attribute ) -> StyledObject < D > {
2018-11-22 03:48:22 +11:00
self . object_style . add_attr ( attr ) ;
2018-01-26 04:26:08 +11:00
self
}
2018-01-28 04:26:08 +11:00
/// Increase the font intensity.
2018-07-02 06:43:43 +10:00
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn bold ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Bold )
}
2018-01-28 04:26:08 +11:00
/// Faint (decreased intensity) (Not widely supported).
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn dim ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Dim )
}
2018-01-28 04:26:08 +11:00
/// Make the font italic (Not widely supported; Sometimes treated as inverse).
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn italic ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Italic )
}
2018-01-28 04:26:08 +11:00
/// Underline font.
2018-07-02 06:43:43 +10:00
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn underlined ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Underlined )
}
2018-12-31 10:13:45 +11:00
/// Invert colours.
#[ cfg(windows) ]
#[ inline(always) ]
pub fn negative ( self ) -> StyledObject < D > {
self . attr ( Attribute ::Negative )
}
2018-01-28 04:26:08 +11:00
/// Slow Blink (less than 150 per minute; not widely supported).
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn slow_blink ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::SlowBlink )
}
2018-01-28 04:26:08 +11:00
/// Rapid Blink (MS-DOS ANSI.SYS; 150+ per minute; not widely supported).
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn rapid_blink ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::RapidBlink )
}
/// Swap foreground and background colors.
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn reverse ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Reverse )
}
2018-01-28 04:26:08 +11:00
/// Hide text (Not widely supported).
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn hidden ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::Hidden )
}
2018-01-28 04:26:08 +11:00
/// Characters legible, but marked for deletion. Not widely supported.
2018-07-02 06:43:43 +10:00
#[ cfg(unix) ]
#[ inline(always) ]
2018-08-12 01:58:15 +10:00
pub fn crossed_out ( self ) -> StyledObject < D > {
2018-07-02 06:43:43 +10:00
self . attr ( Attribute ::CrossedOut )
}
2018-01-04 00:43:54 +11:00
2019-01-28 07:16:14 +11:00
/// This converts an styled object into an `DisplayableObject` witch implements: `Display` and could be used inside the write function of the standard library.
///
/// _StyledObject already implements `Display` right?_
///
/// This is true, however there are some complex issues why this won't work on alternate screen.
/// That is the reason why this functions exists.
/// You could just pass in the 'screen' from your alternate screen to this method and your `StyledObject` will be printed to the alternate screen just fine.
///
/// ```
/// let screen = Screen::default(); /* represents the alternate screen */
/// let styled_object = style("test").with(Color::Yellow);
/// let display_object = styled_object.into_displayable(&screen);
/// println!("Colored text: {}. Default color", display_object);
/// ```
pub fn into_displayable ( self , stdout : & ' a Arc < TerminalOutput > ) -> DisplayableObject < ' a , D > {
DisplayableObject ::new ( stdout , self )
}
2018-11-15 02:53:27 +11:00
/// This could be used to paint the styled object onto the given screen. You have to pass a reference to the screen whereon you want to perform the painting.
2018-08-14 05:04:07 +10:00
///
/// ``` rust
/// style("Some colored text")
/// .with(Color::Blue)
/// .on(Color::Black)
/// .paint(&screen);
/// ```
2018-11-15 02:53:27 +11:00
///
/// You should take not that `StyledObject` implements `Display`. You don't need to call paint unless you are on alternate screen.
/// Checkout `into_displayable()` for more information about this.
2019-01-28 07:16:14 +11:00
pub fn paint ( & self , stdout : & Arc < TerminalOutput > ) -> Result < ( ) > {
let colored_terminal = super ::TerminalColor ::from_output ( stdout ) ;
2018-07-04 06:48:04 +10:00
let mut reset = true ;
if let Some ( bg ) = self . object_style . bg_color {
2019-01-02 08:27:50 +11:00
colored_terminal . set_bg ( bg ) ? ;
2018-07-04 06:48:04 +10:00
reset = true ;
}
if let Some ( fg ) = self . object_style . fg_color {
2019-01-02 08:27:50 +11:00
colored_terminal . set_fg ( fg ) ? ;
2018-07-04 06:48:04 +10:00
reset = true ;
}
2018-11-15 02:53:27 +11:00
for attr in self . object_style . attrs . iter ( ) {
2019-01-28 07:16:14 +11:00
stdout . write_string ( format! ( csi! ( " {}m " ) , * attr as i16 ) ) ? ;
2018-07-04 06:48:04 +10:00
reset = true ;
2018-01-07 07:31:14 +11:00
}
2018-07-29 03:26:35 +10:00
use std ::fmt ::Write ;
2018-08-12 01:58:15 +10:00
let mut content = String ::new ( ) ;
2019-01-02 08:27:50 +11:00
write! ( content , " {} " , self . content ) ? ;
2019-01-28 07:16:14 +11:00
stdout . write_string ( content ) ? ;
stdout . flush ( ) ? ;
2018-07-04 06:48:04 +10:00
if reset {
2019-01-02 08:27:50 +11:00
colored_terminal . reset ( ) ? ;
2018-07-04 06:48:04 +10:00
}
2019-01-02 08:27:50 +11:00
Ok ( ( ) )
2018-07-04 06:48:04 +10:00
}
2018-09-21 06:24:10 +10:00
}
2018-11-15 02:53:27 +11:00
impl < D : Display > Display for StyledObject < D > {
2019-01-02 08:27:50 +11:00
fn fmt ( & self , f : & mut Formatter ) -> result ::Result < ( ) , fmt ::Error > {
2018-11-22 03:48:22 +11:00
let colored_terminal = color ( ) ;
2018-11-15 02:53:27 +11:00
let mut reset = true ;
if let Some ( bg ) = self . object_style . bg_color {
2019-01-02 08:27:50 +11:00
colored_terminal . set_bg ( bg ) . unwrap ( ) ;
2018-11-15 02:53:27 +11:00
reset = true ;
}
if let Some ( fg ) = self . object_style . fg_color {
2019-01-02 08:27:50 +11:00
colored_terminal . set_fg ( fg ) . unwrap ( ) ;
2018-11-15 02:53:27 +11:00
reset = true ;
}
2018-11-26 00:17:11 +11:00
for attr in self . object_style . attrs . iter ( ) {
2019-01-02 08:27:50 +11:00
write! ( f , " {} " , format! ( csi! ( " {}m " ) , * attr as i16 ) ) ? ;
2018-11-26 00:17:11 +11:00
reset = true ;
}
2018-11-15 02:53:27 +11:00
fmt ::Display ::fmt ( & self . content , f ) ? ;
2019-01-02 08:27:50 +11:00
std ::io ::stdout ( ) . flush ( ) . unwrap ( ) ;
2018-11-15 02:53:27 +11:00
if reset {
2019-01-02 08:27:50 +11:00
colored_terminal . reset ( ) . unwrap ( ) ;
std ::io ::stdout ( ) . flush ( ) . unwrap ( ) ;
2018-11-15 02:53:27 +11:00
}
2019-01-28 07:16:14 +11:00
2018-11-15 02:53:27 +11:00
Ok ( ( ) )
}
}
2018-11-29 02:42:05 +11:00
/// This is a wrapper for a styled object on 'alternate screen' so that the styled object could be printed on the 'alternate screen' with the standard write functions in rust.
2018-09-22 05:36:25 +10:00
///
/// ```
/// write! ("some normal text, {} <- some colored text", DisplayableObject::new(&screen, styled_object));
2018-11-15 02:53:27 +11:00
/// println! ("some normal text, {} <- some colored text", DisplayableObject::new(&screen, styled_object));
2018-09-22 05:36:25 +10:00
/// ```
2018-11-22 03:48:22 +11:00
pub struct DisplayableObject < ' a , D : Display + ' a > {
2018-09-22 05:36:25 +10:00
styled_object : StyledObject < D > ,
2019-01-28 07:16:14 +11:00
output : & ' a Arc < TerminalOutput > ,
2018-09-21 06:24:10 +10:00
}
2018-11-22 03:48:22 +11:00
impl < ' a , D : Display + ' a > DisplayableObject < ' a , D > {
2019-01-28 07:16:14 +11:00
pub fn new (
screen : & ' a Arc < TerminalOutput > ,
styled_object : StyledObject < D > ,
) -> DisplayableObject < ' a , D > {
2018-11-22 03:48:22 +11:00
DisplayableObject {
2019-01-28 07:16:14 +11:00
output : screen ,
2018-11-22 03:48:22 +11:00
styled_object ,
}
2018-09-21 06:24:10 +10:00
}
}
2018-11-22 03:48:22 +11:00
impl < ' a , D : Display + ' a > Display for DisplayableObject < ' a , D > {
2019-01-02 08:27:50 +11:00
fn fmt ( & self , _f : & mut Formatter ) -> result ::Result < ( ) , fmt ::Error > {
2019-01-28 07:16:14 +11:00
self . styled_object . paint ( self . output ) . unwrap ( ) ;
2018-11-22 03:48:22 +11:00
Ok ( ( ) )
2018-09-21 06:24:10 +10:00
}
2019-01-03 02:53:47 +11:00
}