diff --git a/demo/app/assets/views/documentation.xml b/demo/app/assets/views/documentation.xml
index 058db68..bcc8aeb 100644
--- a/demo/app/assets/views/documentation.xml
+++ b/demo/app/assets/views/documentation.xml
@@ -105,6 +105,11 @@
Spinners
+
+
+ Switch
+
+
Scrollbar
diff --git a/docs/components/switch.md b/docs/components/switch.md
new file mode 100644
index 0000000..03c82d2
--- /dev/null
+++ b/docs/components/switch.md
@@ -0,0 +1,36 @@
+# Switch
+
+Represent the switching between two states or on-off state.
+
+## Basic
+
+``` switch-basic-demo-xml
+
+```
+
+## Text & icon
+
+With text and icon.
+
+``` switch-text-icon-demo-xml
+
+
+
+
+
+
+
+
+
+```
+
+## Disabled
+
+``` switch-text-icon-demo-xml
+
+
+
+
+
+
+```
diff --git a/include/LCDesign/ui/components.h b/include/LCDesign/ui/components.h
index 6be182a..0c18ebd 100644
--- a/include/LCDesign/ui/components.h
+++ b/include/LCDesign/ui/components.h
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/LCDesign/ui/components/spinner.h b/include/LCDesign/ui/components/spinner.h
index 31cdc48..242f7f2 100644
--- a/include/LCDesign/ui/components/spinner.h
+++ b/include/LCDesign/ui/components/spinner.h
@@ -1,4 +1,4 @@
-/*
+/*
* spinner.h -- Spinner, used to indicate the loading state of a component
* or page.
*
diff --git a/include/LCDesign/ui/components/switch.h b/include/LCDesign/ui/components/switch.h
new file mode 100644
index 0000000..931ed69
--- /dev/null
+++ b/include/LCDesign/ui/components/switch.h
@@ -0,0 +1,49 @@
+/*
+ * switch.h -- Switch, used to represent the switching between two states or
+ * on-off state.
+ *
+ * Copyright (c) 2019, Liu chao All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of LCUI nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LCDESIGN_SWITCH_H_
+#define LCDESIGN_SWITCH_H_
+
+LCUI_API void LCDesign_InitSwitch(void);
+
+LCUI_API LCUI_BOOL Switch_IsChecked(LCUI_Widget w);
+
+LCUI_API void Switch_SetChecked(LCUI_Widget w, LCUI_BOOL checked);
+
+LCUI_API void Switch_SetCheckedText(LCUI_Widget w, const char *text);
+
+LCUI_API void Switch_SetUncheckedText(LCUI_Widget w, const char *text);
+
+LCUI_API void Switch_SetCheckedIcon(LCUI_Widget w, const char *icon_name);
+
+LCUI_API void Switch_SetUncheckedIcon(LCUI_Widget w, const char *icon_name);
+
+#endif
diff --git a/main.vcxproj b/main.vcxproj
index 695e1bd..d750c74 100644
--- a/main.vcxproj
+++ b/main.vcxproj
@@ -1,4 +1,4 @@
-
+
@@ -170,6 +170,7 @@
+
@@ -187,6 +188,7 @@
+
diff --git a/main.vcxproj.filters b/main.vcxproj.filters
index 462ba0c..b7e9896 100644
--- a/main.vcxproj.filters
+++ b/main.vcxproj.filters
@@ -1,4 +1,4 @@
-
+
@@ -15,6 +15,7 @@
+
@@ -32,5 +33,6 @@
+
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
index b366539..3653d9a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -40,6 +40,7 @@ void LCDesign_Init(void)
LCDesign_InitRate();
LCDesign_InitTooltip();
LCDesign_InitSpinner();
+ LCDesign_InitSwitch();
LCDesign_InitPassword();
LCDesign_InitTypograhy();
LCDesign_InitModal();
diff --git a/src/scss/_switch.scss b/src/scss/_switch.scss
new file mode 100644
index 0000000..36c3ffe
--- /dev/null
+++ b/src/scss/_switch.scss
@@ -0,0 +1,52 @@
+.switch-bar {
+ left: -$switch-icon-width;
+ height: 100%;
+ width: $switch-bar-width;
+ position: relative;
+}
+
+.switch-slider {
+ background-color: #fff;
+ width: $switch-slider-width;
+ height: 100%;
+}
+
+.switch-on-block,
+.switch-off-block {
+ width: $switch-icon-width;
+ color: #fff;
+ font-size: $switch-icon-height - 2px;
+ text-align: center;
+ line-height: $switch-icon-height;
+}
+
+.switch-on-block,
+.switch-off-block,
+.switch-slider {
+ display: inline-block;
+}
+
+.switch {
+ width: $switch-width;
+ height: $switch-height;
+ display: inline-block;
+ border: $switch-border-width solid $switch-color;
+ background-color: $switch-color;
+
+ &.checked {
+ border-color: $switch-checked-color;
+ background-color: $switch-checked-color;
+
+ .switch-bar {
+ left: 0;
+ }
+ }
+ &:disabled {
+ opacity: 0.4;
+ }
+}
+
+.switch-text {
+ margin-left: $switch-margin-left;
+ line-height: $switch-height;
+}
diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss
index fe6359d..5f3b9ac 100644
--- a/src/scss/_variables.scss
+++ b/src/scss/_variables.scss
@@ -562,3 +562,17 @@ $close-color: $black !default;
$rate-star-size: $font-size-base * 1.5 !default;
$rate-star-color: $orange !default;
$rate-star-void-color: $gray-400 !default;
+
+
+// Switch
+
+$switch-height: 20px;
+$switch-border-width: 2px;
+$switch-margin-left: $font-size-base / 2;
+$switch-slider-width: $switch-height - $switch-border-width * 2;
+$switch-icon-width: $switch-height + $switch-border-width * 2;
+$switch-icon-height: $switch-height - $switch-border-width * 2;
+$switch-bar-width: $switch-icon-width * 2 + $switch-slider-width;
+$switch-width: $switch-icon-width + $switch-slider-width + $switch-border-width * 2;
+$switch-checked-color: $primary;
+$switch-color: $text-muted;
diff --git a/src/scss/lc-design.scss b/src/scss/lc-design.scss
index fe0743b..a977b78 100644
--- a/src/scss/lc-design.scss
+++ b/src/scss/lc-design.scss
@@ -14,6 +14,7 @@
@import "dropdown";
@import "list-group";
@import "rate";
+@import "switch";
@import "close";
@import "modal";
@import "toasts";
diff --git a/src/ui/components/spinner.c b/src/ui/components/spinner.c
index ab43b6d..b499429 100644
--- a/src/ui/components/spinner.c
+++ b/src/ui/components/spinner.c
@@ -1,4 +1,4 @@
-/*
+/*
* spinner.c -- Spinner, used to indicate the loading state of a component
* or page.
*
diff --git a/src/ui/components/switch.c b/src/ui/components/switch.c
new file mode 100644
index 0000000..d171822
--- /dev/null
+++ b/src/ui/components/switch.c
@@ -0,0 +1,240 @@
+/*
+ * switch.c -- Switch, used to represent the switching between two states or
+ * on-off state.
+ *
+ * Copyright (c) 2019, Liu chao All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of LCUI nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef struct SwitchRec_ {
+ char *checked_text;
+ char *checked_icon;
+ char *unchecked_text;
+ char *unchecked_icon;
+ LCUI_Widget txt_off;
+ LCUI_Widget txt_on;
+ LCUI_Widget slider;
+ LCUI_Widget bar;
+ LCUI_BOOL checked;
+} SwitchRec, *Switch;
+
+static struct SwitchModule {
+ LCUI_WidgetPrototype prototype;
+} self;
+
+static void Switch_OnClick(LCUI_Widget w, LCUI_WidgetEvent e, void *arg)
+{
+ LCUI_WidgetEventRec ev;
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (w->disabled) {
+ return;
+ }
+ Switch_SetChecked(w, !data->checked);
+ LCUI_InitWidgetEvent(&ev, "change");
+ ev.cancel_bubble = TRUE;
+ Widget_TriggerEvent(w, &ev, NULL);
+}
+
+static void Switch_ResetChildren(LCUI_Widget w)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (data->txt_off) {
+ Widget_Destroy(data->txt_off);
+ data->txt_off = NULL;
+ }
+ if (data->txt_on) {
+ Widget_Destroy(data->txt_on);
+ data->txt_on = NULL;
+ }
+ if (data->checked_icon) {
+ data->txt_on = LCUIWidget_New("icon");
+ Icon_SetName(data->txt_on, data->checked_icon);
+ } else {
+ data->txt_on = LCUIWidget_New("textview");
+ if (data->checked_text) {
+ TextView_SetText(data->txt_on, data->checked_text);
+ }
+ }
+ if (data->unchecked_icon) {
+ data->txt_off = LCUIWidget_New("icon");
+ Icon_SetName(data->txt_off, data->unchecked_icon);
+ } else {
+ data->txt_off = LCUIWidget_New("textview");
+ if (data->unchecked_text) {
+ TextView_SetText(data->txt_off, data->unchecked_text);
+ }
+ }
+ Widget_AddClass(data->txt_on, "switch-on-block");
+ Widget_AddClass(data->txt_off, "switch-off-block");
+ Widget_Append(data->bar, data->txt_on);
+ Widget_Append(data->bar, data->slider);
+ Widget_Append(data->bar, data->txt_off);
+}
+
+static void Switch_OnInit(LCUI_Widget w)
+{
+ const size_t data_size = sizeof(SwitchRec);
+ Switch data = Widget_AddData(w, self.prototype, data_size);
+
+ data->checked = FALSE;
+ data->bar = LCUIWidget_New(NULL);
+ data->slider = LCUIWidget_New(NULL);
+ data->checked_text = NULL;
+ data->checked_icon = NULL;
+ data->unchecked_text = NULL;
+ data->unchecked_icon = NULL;
+ data->txt_off = NULL;
+ data->txt_on = NULL;
+ Widget_AddClass(w, "switch");
+ Widget_AddClass(data->bar, "switch-bar");
+ Widget_AddClass(data->slider, "switch-slider");
+ Widget_Append(w, data->bar);
+ Widget_BindEvent(w, "click", Switch_OnClick, NULL, NULL);
+ Switch_ResetChildren(w);
+}
+
+static void Switch_OnSetAttribute(LCUI_Widget w, const char *name,
+ const char *value)
+{
+ if (strcmp(name, "checked-icon") == 0) {
+ Switch_SetCheckedIcon(w, value);
+ } else if (strcmp(name, "unchecked-icon") == 0) {
+ Switch_SetUncheckedIcon(w, value);
+ } else if (strcmp(name, "checked-text") == 0) {
+ Switch_SetCheckedText(w, value);
+ } else if (strcmp(name, "unchecked-text") == 0) {
+ Switch_SetUncheckedText(w, value);
+ } else if (strcmp(name, "checked") == 0) {
+ Switch_SetChecked(w, strcmp(value, "checked") == 0);
+ }
+}
+
+LCUI_BOOL Switch_IsChecked(LCUI_Widget w)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ return data->checked;
+}
+
+void Switch_SetChecked(LCUI_Widget w, LCUI_BOOL checked)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ data->checked = checked;
+ if (data->checked) {
+ Widget_AddClass(w, "checked");
+ } else {
+ Widget_RemoveClass(w, "checked");
+ }
+}
+
+void Switch_SetCheckedText(LCUI_Widget w, const char *text)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (data->checked_text) {
+ free(data->checked_text);
+ data->checked_text = NULL;
+ }
+ if (text) {
+ data->checked_text = strdup2(text);
+ if (data->checked_icon) {
+ free(data->checked_icon);
+ data->checked_icon = NULL;
+ }
+ }
+ Switch_ResetChildren(w);
+}
+
+void Switch_SetUncheckedText(LCUI_Widget w, const char *text)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (data->unchecked_text) {
+ free(data->unchecked_text);
+ data->unchecked_text = NULL;
+ }
+ if (text) {
+ data->unchecked_text = strdup2(text);
+ if (data->unchecked_icon) {
+ free(data->unchecked_icon);
+ data->unchecked_icon = NULL;
+ }
+ }
+ Switch_ResetChildren(w);
+}
+
+void Switch_SetCheckedIcon(LCUI_Widget w, const char *icon_name)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (data->checked_icon) {
+ free(data->checked_icon);
+ data->checked_text = NULL;
+ }
+ if (icon_name) {
+ data->checked_icon = strdup2(icon_name);
+ if (data->checked_text) {
+ free(data->checked_text);
+ data->checked_text = NULL;
+ }
+ }
+ Switch_ResetChildren(w);
+}
+
+void Switch_SetUncheckedIcon(LCUI_Widget w, const char *icon_name)
+{
+ Switch data = Widget_GetData(w, self.prototype);
+
+ if (data->unchecked_icon) {
+ free(data->unchecked_icon);
+ data->unchecked_icon = NULL;
+ }
+ if (icon_name) {
+ data->unchecked_icon = strdup2(icon_name);
+ if (data->unchecked_text) {
+ free(data->unchecked_text);
+ data->unchecked_text = NULL;
+ }
+ }
+ Switch_ResetChildren(w);
+}
+
+void LCDesign_InitSwitch(void)
+{
+ self.prototype = LCUIWidget_NewPrototype("switch", NULL);
+ self.prototype->setattr = Switch_OnSetAttribute;
+ self.prototype->init = Switch_OnInit;
+}