menu.cpp
algorithm
chrono
ftxui/dom/direction.hpp
functional
string
utility
vector
ftxui/component/animation.hpp
ftxui/component/component.hpp
ftxui/component/component_base.hpp
ftxui/component/component_options.hpp
ftxui/component/event.hpp
ftxui/component/mouse.hpp
ftxui/component/screen_interactive.hpp
ftxui/dom/elements.hpp
ftxui/screen/box.hpp
ftxui/screen/color.hpp
ftxui/screen/util.hpp
ftxui/util/ref.hpp
ftxui/component/animation.hpp
ftxui/component/captured_mouse.hpp
ftxui/component/component.hpp
ftxui/component/component_base.hpp
ftxui/component/component_options.hpp
ftxui/component/event.hpp
ftxui/component/mouse.hpp
ftxui/component/receiver.hpp
ftxui/component/screen_interactive.hpp
ftxui/component/task.hpp
ftxui/dom/canvas.hpp
ftxui/dom/deprecated.hpp
ftxui/dom/direction.hpp
ftxui/dom/elements.hpp
ftxui/dom/flexbox_config.hpp
ftxui/dom/linear_gradient.hpp
ftxui/dom/node.hpp
ftxui/dom/requirement.hpp
ftxui/dom/selection.hpp
ftxui/screen/box.hpp
ftxui/screen/color.hpp
ftxui/screen/image.hpp
ftxui/screen/pixel.hpp
ftxui/screen/screen.hpp
ftxui/screen/string.hpp
ftxui/screen/terminal.hpp
ftxui/util/autoreset.hpp
ftxui/util/ref.hpp
src/ftxui/component/menu.cpp
ftxui/screen/util.hpp
algorithm
atomic
chrono
condition_variable
cstddef
cstdint
ftxui/dom/take_any_args.hpp
functional
memory
mutex
optional
queue
sstream
string
thread
unordered_map
utility
variant
vector
ftxui::MenuBase
ftxui

    //Copyright2020ArthurSonzogni.Allrightsreserved.
    //UseofthissourcecodeisgovernedbytheMITlicensethatcanbefoundin
    //theLICENSEfile.
    #include<algorithm>//formax,fill_n,reverse
    #include<chrono>//formilliseconds
    #include<ftxui/dom/direction.hpp>//forDirection,Direction::Down,Direction::Left,Direction::Right,Direction::Up
    #include<functional>//forfunction
    #include<string>//foroperator+,string
    #include<utility>//formove
    #include<vector>//forvector,__alloc_traits<>::value_type

    #include"ftxui/component/animation.hpp"//forAnimator,Linear
    #include"ftxui/component/component.hpp"//forMake,Menu,MenuEntry,Toggle
    #include"ftxui/component/component_base.hpp"//forComponentBase
    #include"ftxui/component/component_options.hpp"//forMenuOption,MenuEntryOption,UnderlineOption,AnimatedColorOption,AnimatedColorsOption,EntryState
    #include"ftxui/component/event.hpp"//forEvent,Event::ArrowDown,Event::ArrowLeft,Event::ArrowRight,Event::ArrowUp,Event::End,Event::Home,Event::PageDown,Event::PageUp,Event::Return,Event::Tab,Event::TabReverse
    #include"ftxui/component/mouse.hpp"//forMouse,Mouse::Left,Mouse::Released,Mouse::WheelDown,Mouse::WheelUp,Mouse::None
    #include"ftxui/component/screen_interactive.hpp"//forComponent
    #include"ftxui/dom/elements.hpp"//foroperator|,Element,reflect,Decorator,nothing,Elements,bgcolor,color,hbox,separatorHSelector,separatorVSelector,vbox,xflex,yflex,text,bold,focus,inverted,select
    #include"ftxui/screen/box.hpp"//forBox
    #include"ftxui/screen/color.hpp"//forColor
    #include"ftxui/screen/util.hpp"//forclamp
    #include"ftxui/util/ref.hpp"//forRef,ConstStringListRef,ConstStringRef

    namespaceftxui{

    namespace{

    ElementDefaultOptionTransform(constEntryState&state){
    std::stringlabel=(state.active?">":"")+state.label;//NOLINT
    Elemente=text(std::move(label));
    if(state.focused){
    e=e|inverted;
    }
    if(state.active){
    e=e|bold;
    }
    returne;
    }

    boolIsInverted(Directiondirection){
    switch(direction){
    caseDirection::Up:
    caseDirection::Left:
    returntrue;
    caseDirection::Down:
    caseDirection::Right:
    returnfalse;
    }
    returnfalse;//NOT_REACHED()
    }

    boolIsHorizontal(Directiondirection){
    switch(direction){
    caseDirection::Left:
    caseDirection::Right:
    returntrue;
    caseDirection::Down:
    caseDirection::Up:
    returnfalse;
    }
    returnfalse;//NOT_REACHED()
    }

    }//namespace

    ///@briefAlistofitems.Theusercannavigatethroughthem.
    ///@ingroupcomponent
    classMenuBase:publicComponentBase,publicMenuOption{
    public:
    explicitMenuBase(constMenuOption&option):MenuOption(option){}

    boolIsHorizontal(){returnftxui::IsHorizontal(direction);}
    voidOnChange(){
    if(on_change){
    on_change();
    }
    }

    voidOnEnter(){
    if(on_enter){
    on_enter();
    }
    }

    voidClamp(){
    if(selected()!=selected_previous_){
    SelectedTakeFocus();
    }
    boxes_.resize(size());
    selected()=util::clamp(selected(),0,size()-1);
    selected_previous_=util::clamp(selected_previous_,0,size()-1);
    selected_focus_=util::clamp(selected_focus_,0,size()-1);
    focused_entry()=util::clamp(focused_entry(),0,size()-1);
    }

    voidOnAnimation(animation::Params&params)override{
    animator_first_.OnAnimation(params);
    animator_second_.OnAnimation(params);
    for(auto&animator:animator_background_){
    animator.OnAnimation(params);
    }
    for(auto&animator:animator_foreground_){
    animator.OnAnimation(params);
    }
    }

    ElementOnRender()override{
    Clamp();
    UpdateAnimationTarget();

    Elementselements;
    constboolis_menu_focused=Focused();
    if(elements_prefix){
    elements.push_back(elements_prefix());
    }
    elements.reserve(size());
    for(inti=0;i<size();++i){
    if(i!=0&&elements_infix){
    elements.push_back(elements_infix());
    }
    constboolis_focused=(focused_entry()==i)&&is_menu_focused;
    constboolis_selected=(selected()==i);

    constEntryStatestate={
    entries[i],false,is_selected,is_focused,i,
    };

    Elementelement=(entries_option.transform?entries_option.transform
    :DefaultOptionTransform)//
    (state);
    if(selected_focus_==i){
    element|=focus;
    }
    element|=AnimatedColorStyle(i);
    element|=reflect(boxes_[i]);
    elements.push_back(element);
    }
    if(elements_postfix){
    elements.push_back(elements_postfix());
    }

    if(IsInverted(direction)){
    std::reverse(elements.begin(),elements.end());
    }

    constElementbar=IsHorizontal()
    ?hbox(std::move(elements),selected_focus_)
    :vbox(std::move(elements),selected_focus_);

    if(!underline.enabled){
    returnbar|reflect(box_);
    }

    if(IsHorizontal()){
    returnvbox({
    bar|xflex,
    separatorHSelector(first_,second_,//
    underline.color_active,
    underline.color_inactive),
    })|
    reflect(box_);
    }else{
    returnhbox({
    separatorVSelector(first_,second_,//
    underline.color_active,
    underline.color_inactive),
    bar|yflex,
    })|
    reflect(box_);
    }
    }

    voidSelectedTakeFocus(){
    selected_previous_=selected();
    selected_focus_=selected();
    }

    voidOnUp(){
    switch(direction){
    caseDirection::Up:
    selected()++;
    break;
    caseDirection::Down:
    selected()--;
    break;
    caseDirection::Left:
    caseDirection::Right:
    break;
    }
    }

    voidOnDown(){
    switch(direction){
    caseDirection::Up:
    selected()--;
    break;
    caseDirection::Down:
    selected()++;
    break;
    caseDirection::Left:
    caseDirection::Right:
    break;
    }
    }

    voidOnLeft(){
    switch(direction){
    caseDirection::Left:
    selected()++;
    break;
    caseDirection::Right:
    selected()--;
    break;
    caseDirection::Down:
    caseDirection::Up:
    break;
    }
    }

    voidOnRight(){
    switch(direction){
    caseDirection::Left:
    selected()--;
    break;
    caseDirection::Right:
    selected()++;
    break;
    caseDirection::Down:
    caseDirection::Up:
    break;
    }
    }

    //NOLINTNEXTLINE(readability-function-cognitive-complexity)
    boolOnEvent(Eventevent)override{
    Clamp();
    if(!CaptureMouse(event)){
    returnfalse;
    }

    if(event.is_mouse()){
    returnOnMouseEvent(event);
    }

    if(Focused()){
    constintold_selected=selected();
    if(event==Event::ArrowUp||event==Event::Character('k')){
    OnUp();
    }
    if(event==Event::ArrowDown||event==Event::Character('j')){
    OnDown();
    }
    if(event==Event::ArrowLeft||event==Event::Character('h')){
    OnLeft();
    }
    if(event==Event::ArrowRight||event==Event::Character('l')){
    OnRight();
    }
    if(event==Event::PageUp){
    selected()-=box_.y_max-box_.y_min;
    }
    if(event==Event::PageDown){
    selected()+=box_.y_max-box_.y_min;
    }
    if(event==Event::Home){
    selected()=0;
    }
    if(event==Event::End){
    selected()=size()-1;
    }
    if(event==Event::Tab&&size()){
    selected()=(selected()+1)%size();
    }
    if(event==Event::TabReverse&&size()){
    selected()=(selected()+size()-1)%size();
    }

    selected()=util::clamp(selected(),0,size()-1);

    if(selected()!=old_selected){
    focused_entry()=selected();
    SelectedTakeFocus();
    OnChange();
    returntrue;
    }
    }

    if(event==Event::Return){
    OnEnter();
    returntrue;
    }

    returnfalse;
    }

    boolOnMouseEvent(Eventevent){
    if(event.mouse().button==Mouse::WheelDown||
    event.mouse().button==Mouse::WheelUp){
    returnOnMouseWheel(event);
    }

    if(event.mouse().button!=Mouse::None&&
    event.mouse().button!=Mouse::Left){
    returnfalse;
    }
    if(!CaptureMouse(event)){
    returnfalse;
    }
    for(inti=0;i<size();++i){
    if(!boxes_[i].Contain(event.mouse().x,event.mouse().y)){
    continue;
    }

    TakeFocus();
    focused_entry()=i;

    if(event.mouse().button==Mouse::Left&&
    event.mouse().motion==Mouse::Pressed){
    if(selected()!=i){
    selected()=i;
    selected_previous_=selected();
    OnChange();
    }
    returntrue;
    }
    }
    returnfalse;
    }

    boolOnMouseWheel(Eventevent){
    if(!box_.Contain(event.mouse().x,event.mouse().y)){
    returnfalse;
    }
    constintold_selected=selected();

    if(event.mouse().button==Mouse::WheelUp){
    selected()--;
    }
    if(event.mouse().button==Mouse::WheelDown){
    selected()++;
    }

    selected()=util::clamp(selected(),0,size()-1);

    if(selected()!=old_selected){
    SelectedTakeFocus();
    OnChange();
    }
    returntrue;
    }

    voidUpdateAnimationTarget(){
    UpdateColorTarget();
    UpdateUnderlineTarget();
    }

    voidUpdateColorTarget(){
    if(size()!=int(animation_background_.size())){
    animation_background_.resize(size());
    animation_foreground_.resize(size());
    animator_background_.clear();
    animator_foreground_.clear();

    constintlen=size();
    animator_background_.reserve(len);
    animator_foreground_.reserve(len);
    for(inti=0;i<len;++i){
    animation_background_[i]=0.F;
    animation_foreground_[i]=0.F;
    animator_background_.emplace_back(&animation_background_[i],0.F,
    std::chrono::milliseconds(0),
    animation::easing::Linear);
    animator_foreground_.emplace_back(&animation_foreground_[i],0.F,
    std::chrono::milliseconds(0),
    animation::easing::Linear);
    }
    }

    constboolis_menu_focused=Focused();
    for(inti=0;i<size();++i){
    constboolis_focused=(focused_entry()==i)&&is_menu_focused;
    constboolis_selected=(selected()==i);
    floattarget=is_selected?1.F:is_focused?0.5F:0.F;//NOLINT
    if(animator_background_[i].to()!=target){
    animator_background_[i]=animation::Animator(
    &animation_background_[i],target,
    entries_option.animated_colors.background.duration,
    entries_option.animated_colors.background.function);
    animator_foreground_[i]=animation::Animator(
    &animation_foreground_[i],target,
    entries_option.animated_colors.foreground.duration,
    entries_option.animated_colors.foreground.function);
    }
    }
    }

    DecoratorAnimatedColorStyle(inti){
    Decoratorstyle=nothing;
    if(entries_option.animated_colors.foreground.enabled){
    style=style|color(Color::Interpolate(
    animation_foreground_[i],
    entries_option.animated_colors.foreground.inactive,
    entries_option.animated_colors.foreground.active));
    }

    if(entries_option.animated_colors.background.enabled){
    style=style|bgcolor(Color::Interpolate(
    animation_background_[i],
    entries_option.animated_colors.background.inactive,
    entries_option.animated_colors.background.active));
    }
    returnstyle;
    }

    voidUpdateUnderlineTarget(){
    if(!underline.enabled){
    return;
    }

    if(FirstTarget()==animator_first_.to()&&
    SecondTarget()==animator_second_.to()){
    return;
    }

    if(FirstTarget()>=animator_first_.to()){
    animator_first_=animation::Animator(
    &first_,FirstTarget(),underline.follower_duration,
    underline.follower_function,underline.follower_delay);

    animator_second_=animation::Animator(
    &second_,SecondTarget(),underline.leader_duration,
    underline.leader_function,underline.leader_delay);
    }else{
    animator_first_=animation::Animator(
    &first_,FirstTarget(),underline.leader_duration,
    underline.leader_function,underline.leader_delay);

    animator_second_=animation::Animator(
    &second_,SecondTarget(),underline.follower_duration,
    underline.follower_function,underline.follower_delay);
    }
    }

    boolFocusable()constfinal{returnentries.size();}
    intsize()const{returnint(entries.size());}
    floatFirstTarget(){
    if(boxes_.empty()){
    return0.F;
    }
    constintvalue=IsHorizontal()?boxes_[selected()].x_min-box_.x_min
    :boxes_[selected()].y_min-box_.y_min;
    returnfloat(value);
    }
    floatSecondTarget(){
    if(boxes_.empty()){
    return0.F;
    }
    constintvalue=IsHorizontal()?boxes_[selected()].x_max-box_.x_min
    :boxes_[selected()].y_max-box_.y_min;
    returnfloat(value);
    }

    protected:
    intselected_previous_=selected();
    intselected_focus_=selected();

    //Mouseclicksupport:
    std::vector<Box>boxes_;
    Boxbox_;

    //Animationsupport:
    floatfirst_=0.F;
    floatsecond_=0.F;
    animation::Animatoranimator_first_=animation::Animator(&first_,0.F);
    animation::Animatoranimator_second_=animation::Animator(&second_,0.F);
    std::vector<animation::Animator>animator_background_;
    std::vector<animation::Animator>animator_foreground_;
    std::vector<float>animation_background_;
    std::vector<float>animation_foreground_;
    };

    ///@briefAlistoftext.Thefocusedelementisselected.
    ///@paramoptionastructurecontainingalltheparamters.
    ///@ingroupcomponent
    ///
    ///###Example
    ///
    ///```cpp
    ///autoscreen=ScreenInteractive::TerminalOutput();
    ///std::vector<std::string>entries={
    ///"entry1",
    ///"entry2",
    ///"entry3",
    ///};
    ///intselected=0;
    ///automenu=Menu({
    ///.entries=&entries,
    ///.selected=&selected,
    ///});
    ///screen.Loop(menu);
    ///```
    ///
    ///###Output
    ///
    ///```bash
    ///>entry1
    ///entry2
    ///entry3
    ///```
    //NOLINTNEXTLINE
    ComponentMenu(MenuOptionoption){
    returnMake<MenuBase>(std::move(option));
    }

    ///@briefAlistoftext.Thefocusedelementisselected.
    ///@paramentriesThelistofentriesinthemenu.
    ///@paramselectedTheindexofthecurrentlyselectedelement.
    ///@paramoptionAdditionaloptionalparameters.
    ///@ingroupcomponent
    ///
    ///###Example
    ///
    ///```cpp
    ///autoscreen=ScreenInteractive::TerminalOutput();
    ///std::vector<std::string>entries={
    ///"entry1",
    ///"entry2",
    ///"entry3",
    ///};
    ///intselected=0;
    ///automenu=Menu(&entries,&selected);
    ///screen.Loop(menu);
    ///```
    ///
    ///###Output
    ///
    ///```bash
    ///>entry1
    ///entry2
    ///entry3
    ///```
    ComponentMenu(ConstStringListRefentries,int*selected,MenuOptionoption){
    option.entries=std::move(entries);
    option.selected=selected;
    returnMenu(option);
    }

    ///@briefAnhorizontallistofelements.Theusercannavigatethroughthem.
    ///@paramentriesThelistofselectableentriestodisplay.
    ///@paramselectedReferencetheselectedentry.
    ///Seealso|Menu|.
    ///@ingroupcomponent
    ComponentToggle(ConstStringListRefentries,int*selected){
    returnMenu(std::move(entries),selected,MenuOption::Toggle());
    }

    ///@briefAspecificmenuentry.TheycanbeputintoaContainer::Verticalto
    ///formamenu.
    ///@paramlabelThetextdrawnrepresentingthiselement.
    ///@paramoptionAdditionaloptionalparameters.
    ///@ingroupcomponent
    ///
    ///###Example
    ///
    ///```cpp
    ///autoscreen=ScreenInteractive::TerminalOutput();
    ///intselected=0;
    ///automenu=Container::Vertical({
    ///MenuEntry("entry1"),
    ///MenuEntry("entry2"),
    ///MenuEntry("entry3"),
    ///},&selected);
    ///screen.Loop(menu);
    ///```
    ///
    ///###Output
    ///
    ///```bash
    ///>entry1
    ///entry2
    ///entry3
    ///```
    ComponentMenuEntry(ConstStringReflabel,MenuEntryOptionoption){
    option.label=std::move(label);
    returnMenuEntry(std::move(option));
    }

    ///@briefAspecificmenuentry.TheycanbeputintoaContainer::Verticalto
    ///formamenu.
    ///@paramoptionTheparameters.
    ///@ingroupcomponent
    ///
    ///###Example
    ///
    ///```cpp
    ///autoscreen=ScreenInteractive::TerminalOutput();
    ///intselected=0;
    ///automenu=Container::Vertical({
    ///MenuEntry({.label="entry1"}),
    ///MenuEntry({.label="entry2"}),
    ///MenuEntry({.label="entry3"}),
    ///},&selected);
    ///screen.Loop(menu);
    ///```
    ///
    ///###Output
    ///
    ///```bash
    ///>entry1
    ///entry2
    ///entry3
    ///```
    ComponentMenuEntry(MenuEntryOptionoption){
    classImpl:publicComponentBase,publicMenuEntryOption{
    public:
    explicitImpl(MenuEntryOptionoption)
    :MenuEntryOption(std::move(option)){}

    private:
    ElementOnRender()override{
    constboolis_focused=Focused();
    UpdateAnimationTarget();

    constEntryStatestate{
    label(),false,hovered_,is_focused,Index(),
    };

    Elementelement=(transform?transform:DefaultOptionTransform)//
    (state);

    if(is_focused){
    element|=focus;
    }

    returnelement|AnimatedColorStyle()|reflect(box_);
    }

    voidUpdateAnimationTarget(){
    constboolfocused=Focused();
    floattarget=focused?1.F:hovered_?0.5F:0.F;//NOLINT
    if(target==animator_background_.to()){
    return;
    }
    animator_background_=animation::Animator(
    &animation_background_,target,animated_colors.background.duration,
    animated_colors.background.function);
    animator_foreground_=animation::Animator(
    &animation_foreground_,target,animated_colors.foreground.duration,
    animated_colors.foreground.function);
    }

    DecoratorAnimatedColorStyle(){
    Decoratorstyle=nothing;
    if(animated_colors.foreground.enabled){
    style=style|
    color(Color::Interpolate(animation_foreground_,
    animated_colors.foreground.inactive,
    animated_colors.foreground.active));
    }

    if(animated_colors.background.enabled){
    style=style|
    bgcolor(Color::Interpolate(animation_background_,
    animated_colors.background.inactive,
    animated_colors.background.active));
    }
    returnstyle;
    }

    boolFocusable()constoverride{returntrue;}
    boolOnEvent(Eventevent)override{
    if(!event.is_mouse()){
    returnfalse;
    }

    hovered_=box_.Contain(event.mouse().x,event.mouse().y);

    if(!hovered_){
    returnfalse;
    }

    if(event.mouse().button==Mouse::Left&&
    event.mouse().motion==Mouse::Pressed){
    TakeFocus();
    returntrue;
    }

    returnfalse;
    }

    voidOnAnimation(animation::Params&params)override{
    animator_background_.OnAnimation(params);
    animator_foreground_.OnAnimation(params);
    }

    Boxbox_;
    boolhovered_=false;

    floatanimation_background_=0.F;
    floatanimation_foreground_=0.F;
    animation::Animatoranimator_background_=
    animation::Animator(&animation_background_,0.F);
    animation::Animatoranimator_foreground_=
    animation::Animator(&animation_foreground_,0.F);
    };

    returnMake<Impl>(std::move(option));
    }

    }//namespaceftxui