# TabBar

> एनिमेटेड संकेतक और प्रति-पृष्ठ snap के साथ क्षैतिज रूप से रोलेबल सामग्री वाली टैब बार।

Source: https://crossfly.dev/hi/docs/ui/widgets/tabbar — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# TabBar

`TabBar` एक टैब बार है: बटनों की एक पट्टी (tab strip) + एक **क्षैतिज रूप से रोलेबल** सामग्री पैनल जिसमें **प्रति-पृष्ठ snap** होता है (`PageScrollEngine` के माध्यम से)। किसी टैब को स्पर्श करने पर सामग्री संबंधित पृष्ठ तक रोल होती है; सामग्री को **खींचने** से सक्रिय टैब बदलती है। एक **एनिमेटेड संकेतक** सक्रिय टैब का अनुसरण करता है।

सौंदर्य (संकेतक, शीर्ष या तल पर टैब, रंग) सक्रिय `Skin` (`TabBarStyle`) से आता है। प्रत्येक टैब को एक `TabBar.Tab` (टेक्स्ट, आइकन या दोनों) द्वारा वर्णित किया जाता है।

> 🟡 · **पैकेज:** `dev.crossfly.ui` · **विस्तार करता है:** `Component` · **शैली:** `TabBarStyle` (`getSkin().tabBar()` के माध्यम से)

## कब उपयोग करें
- किसी स्क्रीन के 2–5 अनुभागों के बीच नेविगेशन (Home / खोजें / प्रोफ़ाइल)।
- पृष्ठों के बीच swipe इशारे + समन्वित टैब के साथ पृष्ठांकित सामग्री।

## कब उपयोग न करें
- बहुत सारे अनुभाग जो समाते नहीं (पट्टी रोल होती है, पर कम सुपाठ्य रहती है) → किसी अन्य नेविगेशन पर विचार करें।

## रूप-रंग
समान चौड़ाई (`1/n`) की टैब पट्टी; यदि वे न समाएँ, तो पट्टी क्षैतिज रूप से रोलेबल हो जाती है और टैब स्वचालित चौड़ाई ले लेते हैं। संकेतक सक्रिय टैब के नीचे (या ऊपर, यदि `tabsAtBottom`) सरकता है। सामग्री पैनल snap के साथ एक बार में एक पृष्ठ दिखाता है।

## पूरा उदाहरण (कॉपी करें, पेस्ट करें और चलाएँ)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** चलाने योग्य मुख्य स्क्रीन। launcher (classname) की ओर संकेत करें और चलाएँ: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // हर टैब के लिए एक कंटेंट व्यू (गिनती टैब की संख्या से मेल खानी ही चाहिए)।
      Label resumo    = new Label("Conteúdo do Resumo");
      Label detalhes  = new Label("Conteúdo dos Detalhes");
      Label historico = new Label("Conteúdo do Histórico");

      TabBar tabs = new TabBar(
         new TabBar.Tab("Resumo"),
         new TabBar.Tab("Detalhes"),
         new TabBar.Tab("Histórico"));
      tabs.setContent(resumo, detalhes, historico);
      tabs.onTabChanged(i -> System.out.println("aba " + i));

      add(LAY_FILL, tabs);   // TabBar बची हुई ऊँचाई सोख लेती है
   }
}
```

> नीचे के अंश **केवल मूल भाग** दिखाते हैं (जो `onShow()` के अंदर जाता है)। चलाने के लिए, उन्हें ऊपर दिए `MainWindow` कंकाल में रखें।

## बुनियादी उपयोग
```java
TabBar tabs = new TabBar(
   new TabBar.Tab("Home",   new MaterialIcon("")),
   new TabBar.Tab("Buscar", new MaterialIcon("")),
   new TabBar.Tab("Perfil", new MaterialIcon("")));
tabs.setContent(homeView, searchView, profileView);
tabs.onTabChanged(i -> System.out.println("aba " + i));
add(LAY_FILL, tabs);
```

## Constructors
| Constructor | विवरण |
|---|---|
| `TabBar(Tab... tabs)` | टैब के साथ बार बनाता है (`activeIndex = 0`) |

### `TabBar.Tab` (टैब वर्णनकर्ता)
| Constructor | परिणाम |
|---|---|
| `Tab(String text)` | केवल टेक्स्ट (`TextPos.NONE`) |
| `Tab(Icon icon)` | केवल आइकन (`TextPos.NONE`) |
| `Tab(String text, Icon icon)` | टेक्स्ट + आइकन (`TextPos.TEXT_AT_BOTTOM`) |
| `Tab(String text, Icon icon, TextPos textPos)` | कस्टम स्थिति के साथ टेक्स्ट + आइकन |

## गुण और अनुकूलन
| गुण / विधि | प्रकार | क्या करता है |
|---|---|---|
| `setContent(Component...)` | `TabBar` | प्रति टैब सामग्री (गिनती टैब से **मेल** खानी चाहिए, अन्यथा `IllegalArgumentException`) |
| `setActiveTab(int)` | `TabBar` | किसी टैब को प्रोग्रामेटिक रूप से सक्रिय करता है (एनिमेशन/scroll के साथ); अमान्य या पहले से सक्रिय होने पर no-op |
| `getActiveIndex()` | `int` | सक्रिय टैब का सूचकांक |
| `getTabs()` | `Tab[]` | टैब के वर्णनकर्ता |
| `getStrip()` / `getTabButtons()` / `getContentPanel()` | — | आंतरिक भागों तक पहुँच |
| `getIndicatorX()` / `getIndicatorW()` | `float` | संकेतक की वर्तमान स्थिति/चौड़ाई (`Design` में चित्रण के लिए) |

## Callbacks और घटनाएँ
| Callback | कब ट्रिगर होता है |
|---|---|
| `onTabChanged(TabChangedListener)` | टैब बदलने पर, स्पर्श, swipe या `setActiveTab(...)` द्वारा |

`TabChangedListener` कार्यात्मक है: `void onTabChanged(int index)`।

## अवस्थाएँ
- **सक्रिय** — एक बार में केवल एक टैब; संकेतक उस तक एनिमेट होता है (अवधि `TabBarStyle.switchDuration`)।
- **उपयोगकर्ता swipe** — संकेतक पड़ोसी टैब के बीच निरंतर प्रक्षेपित करता है; दूसरे पृष्ठ पर पहुँचते ही `activeIndex` बदलता है और `onTabChanged` ट्रिगर होता है।
- **प्रोग्रामेटिक** — `setActiveTab`/स्पर्श scroll एनिमेशन के लक्ष्य तक पहुँचने तक समन्वयन को लॉक कर देते हैं।

### केवल-टेक्स्ट टैब
```java
TabBar tabs = new TabBar(
   new TabBar.Tab("Resumo"),
   new TabBar.Tab("Detalhes"),
   new TabBar.Tab("Histórico"));
tabs.setContent(resumoView, detalhesView, historicoView);
add(LAY_FILL, tabs);
```

### टैब का प्रोग्रामेटिक परिवर्तन
```java
TabBar tabs = new TabBar(new TabBar.Tab("A"), new TabBar.Tab("B"), new TabBar.Tab("C"));
tabs.setContent(viewA, viewB, viewC);
add(LAY_FILL, tabs);
// ... बाद में:
tabs.setActiveTab(2);  // C टैब तक एनिमेट होता है
```

## अच्छे अभ्यास
- `setContent(...)` की गिनती टैब की संख्या से **मेल** खानी चाहिए।
- तल पर टैब (iOS शैली) के लिए, ऐसा `Skin` उपयोग करें जिसका `TabBarStyle.tabsAtBottom = true` हो — layout और संकेतक स्वयं समायोजित हो जाते हैं।
- टैब पर स्थिर चौड़ाई बाध्य न करें: यदि वे न समाएँ, तो पट्टी स्वयं रोल हो जाती है।

## सामान्य गलतियाँ
- **गलत गिनती के साथ `setContent`** → `IllegalArgumentException`। प्रति टैब ठीक एक view पास करें।
- **पहले से सक्रिय सूचकांक के लिए `setActiveTab` द्वारा `onTabChanged` ट्रिगर होने की अपेक्षा** — `index == activeIndex` होने पर यह no-op है।

## यह भी देखें
- क्षेत्र गाइड: [`ui/`](/docs/ui) · रेसिपी: [`cookbook/`](/docs/cookbook)
